@haaaiawd/anws 1.2.5 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +230 -174
- package/bin/cli.js +22 -9
- package/lib/adapters/index.js +157 -0
- package/lib/agents.js +136 -1
- package/lib/changelog.js +187 -0
- package/lib/copy.js +72 -1
- package/lib/diff.js +270 -0
- package/lib/init.js +150 -125
- package/lib/install-state.js +195 -0
- package/lib/manifest.js +184 -42
- package/lib/output.js +185 -13
- package/lib/prompt.js +284 -0
- package/lib/resources/index.js +27 -0
- package/lib/update.js +291 -83
- package/package.json +10 -6
- package/templates/.agents/skills/concept-modeler/SKILL.md +176 -0
- package/templates/{.agent → .agents}/skills/design-reviewer/SKILL.md +6 -6
- package/templates/.agents/skills/nexus-mapper/SKILL.md +306 -0
- package/templates/.agents/skills/nexus-mapper/references/language-customization.md +164 -0
- package/templates/.agents/skills/nexus-mapper/references/output-schema.md +298 -0
- package/templates/.agents/skills/nexus-mapper/references/probe-protocol.md +246 -0
- package/templates/.agents/skills/nexus-mapper/scripts/extract_ast.py +706 -0
- package/templates/.agents/skills/nexus-mapper/scripts/git_detective.py +194 -0
- package/templates/.agents/skills/nexus-mapper/scripts/languages.json +127 -0
- package/templates/.agents/skills/nexus-mapper/scripts/query_graph.py +556 -0
- package/templates/.agents/skills/nexus-mapper/scripts/requirements.txt +6 -0
- package/templates/{.agent → .agents}/skills/report-template/SKILL.md +11 -14
- package/templates/.agents/skills/report-template/references/REPORT_TEMPLATE.md +100 -0
- package/templates/{.agent → .agents}/skills/runtime-inspector/SKILL.md +1 -1
- package/templates/.agents/skills/sequential-thinking/SKILL.md +179 -0
- package/templates/.agents/skills/spec-writer/SKILL.md +108 -0
- package/templates/{.agent → .agents}/skills/spec-writer/references/prd_template.md +1 -1
- package/templates/{.agent → .agents}/skills/system-architect/SKILL.md +3 -3
- package/templates/.agents/skills/system-architect/references/rfc_template.md +59 -0
- package/templates/{.agent → .agents}/skills/system-designer/SKILL.md +6 -6
- package/templates/{.agent → .agents}/skills/system-designer/references/system-design-template.md +75 -25
- package/templates/{.agent → .agents}/skills/task-planner/SKILL.md +1 -1
- package/templates/.agents/skills/task-planner/references/TASK_TEMPLATE.md +144 -0
- package/templates/{.agent → .agents}/skills/task-reviewer/SKILL.md +4 -3
- package/templates/{.agent → .agents}/skills/tech-evaluator/SKILL.md +2 -2
- package/templates/{.agent → .agents}/skills/tech-evaluator/references/ADR_TEMPLATE.md +10 -0
- package/templates/{.agent → .agents}/workflows/blueprint.md +32 -27
- package/templates/{.agent → .agents}/workflows/challenge.md +21 -15
- package/templates/{.agent → .agents}/workflows/change.md +23 -14
- package/templates/{.agent → .agents}/workflows/craft.md +8 -19
- package/templates/{.agent → .agents}/workflows/design-system.md +81 -54
- package/templates/{.agent → .agents}/workflows/explore.md +6 -19
- package/templates/{.agent → .agents}/workflows/forge.md +30 -32
- package/templates/{.agent → .agents}/workflows/genesis.md +68 -56
- package/templates/.agents/workflows/probe.md +168 -0
- package/templates/{.agent → .agents}/workflows/quickstart.md +7 -12
- package/templates/.agents/workflows/upgrade.md +192 -0
- package/templates/AGENTS.md +66 -45
- package/templates/.agent/skills/build-inspector/SKILL.md +0 -83
- package/templates/.agent/skills/complexity-guard/SKILL.md +0 -71
- package/templates/.agent/skills/complexity-guard/references/anti_patterns.md +0 -21
- package/templates/.agent/skills/concept-modeler/SKILL.md +0 -112
- package/templates/.agent/skills/concept-modeler/prompts/GLOSSARY_PROMPT.md +0 -40
- package/templates/.agent/skills/concept-modeler/references/ENTITY_EXTRACTION_PROMPT.md +0 -299
- package/templates/.agent/skills/concept-modeler/scripts/glossary_gen.py +0 -66
- package/templates/.agent/skills/git-forensics/SKILL.md +0 -74
- package/templates/.agent/skills/git-forensics/references/ANALYSIS_METHODOLOGY.md +0 -193
- package/templates/.agent/skills/git-forensics/scripts/__pycache__/git_forensics.cpython-313.pyc +0 -0
- package/templates/.agent/skills/git-forensics/scripts/git_forensics.py +0 -615
- package/templates/.agent/skills/git-forensics/scripts/git_hotspots.py +0 -118
- package/templates/.agent/skills/report-template/references/REPORT_TEMPLATE.md +0 -100
- package/templates/.agent/skills/spec-writer/SKILL.md +0 -108
- package/templates/.agent/skills/system-architect/references/rfc_template.md +0 -59
- package/templates/.agent/skills/task-planner/references/TASK_TEMPLATE.md +0 -144
- package/templates/.agent/workflows/scout.md +0 -139
- /package/templates/{.agent → .agents}/skills/system-designer/references/system-design-detail-template.md +0 -0
package/lib/diff.js
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { c } = require('./output');
|
|
6
|
+
|
|
7
|
+
async function pathExists(targetPath) {
|
|
8
|
+
return fs.access(targetPath).then(() => true).catch(() => false);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function readTextOrEmpty(filePath) {
|
|
12
|
+
const exists = await pathExists(filePath);
|
|
13
|
+
if (!exists) return '';
|
|
14
|
+
return fs.readFile(filePath, 'utf8');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function normalize(text) {
|
|
18
|
+
return text.replace(/\r\n/g, '\n');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function toLines(text) {
|
|
22
|
+
const lines = normalize(text).split('\n');
|
|
23
|
+
while (lines.length > 0 && lines[lines.length - 1] === '') {
|
|
24
|
+
lines.pop();
|
|
25
|
+
}
|
|
26
|
+
return lines;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function createLineDiff(oldContent, newContent, maxPairs = 8) {
|
|
30
|
+
const before = toLines(oldContent);
|
|
31
|
+
const after = toLines(newContent);
|
|
32
|
+
const pairs = [];
|
|
33
|
+
let beforeIndex = 0;
|
|
34
|
+
let afterIndex = 0;
|
|
35
|
+
|
|
36
|
+
while (beforeIndex < before.length || afterIndex < after.length) {
|
|
37
|
+
const oldLine = before[beforeIndex];
|
|
38
|
+
const newLine = after[afterIndex];
|
|
39
|
+
|
|
40
|
+
if (oldLine === newLine) {
|
|
41
|
+
beforeIndex += 1;
|
|
42
|
+
afterIndex += 1;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const nextOldLine = before[beforeIndex + 1];
|
|
47
|
+
const nextNewLine = after[afterIndex + 1];
|
|
48
|
+
|
|
49
|
+
if (nextOldLine === newLine) {
|
|
50
|
+
pairs.push({
|
|
51
|
+
oldLineNumber: oldLine === undefined ? null : beforeIndex + 1,
|
|
52
|
+
newLineNumber: null,
|
|
53
|
+
oldText: oldLine === undefined ? '' : oldLine,
|
|
54
|
+
newText: '',
|
|
55
|
+
kind: 'removed'
|
|
56
|
+
});
|
|
57
|
+
beforeIndex += 1;
|
|
58
|
+
if (pairs.length >= maxPairs) break;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (oldLine === nextNewLine) {
|
|
63
|
+
pairs.push({
|
|
64
|
+
oldLineNumber: null,
|
|
65
|
+
newLineNumber: newLine === undefined ? null : afterIndex + 1,
|
|
66
|
+
oldText: '',
|
|
67
|
+
newText: newLine === undefined ? '' : newLine,
|
|
68
|
+
kind: 'added'
|
|
69
|
+
});
|
|
70
|
+
afterIndex += 1;
|
|
71
|
+
if (pairs.length >= maxPairs) break;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
pairs.push({
|
|
76
|
+
oldLineNumber: oldLine === undefined ? null : beforeIndex + 1,
|
|
77
|
+
newLineNumber: newLine === undefined ? null : afterIndex + 1,
|
|
78
|
+
oldText: oldLine === undefined ? '' : oldLine,
|
|
79
|
+
newText: newLine === undefined ? '' : newLine,
|
|
80
|
+
kind:
|
|
81
|
+
oldLine === undefined
|
|
82
|
+
? 'added'
|
|
83
|
+
: newLine === undefined
|
|
84
|
+
? 'removed'
|
|
85
|
+
: 'modified'
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
beforeIndex += 1;
|
|
89
|
+
afterIndex += 1;
|
|
90
|
+
|
|
91
|
+
if (pairs.length >= maxPairs) break;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return pairs;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function formatLineNumber(value) {
|
|
98
|
+
return value === null ? '-' : String(value);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function toLegacyManagedPath(rel) {
|
|
102
|
+
if (!rel.startsWith('.agents/')) return rel;
|
|
103
|
+
return `.agent/${rel.slice('.agents/'.length)}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function resolveExistingManagedPath(cwd, rel) {
|
|
107
|
+
const primaryPath = path.join(cwd, rel);
|
|
108
|
+
if (await pathExists(primaryPath)) {
|
|
109
|
+
return { file: rel, absolutePath: primaryPath, sourceKind: 'current' };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const legacyRel = toLegacyManagedPath(rel);
|
|
113
|
+
if (legacyRel !== rel) {
|
|
114
|
+
const legacyPath = path.join(cwd, legacyRel);
|
|
115
|
+
if (await pathExists(legacyPath)) {
|
|
116
|
+
return { file: rel, absolutePath: legacyPath, sourceKind: 'legacy' };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return { file: rel, absolutePath: primaryPath, sourceKind: 'missing' };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function collectManagedFileDiffs({
|
|
124
|
+
cwd,
|
|
125
|
+
managedFiles,
|
|
126
|
+
projectionPlan = [],
|
|
127
|
+
projectionEntries = [],
|
|
128
|
+
srcAgents,
|
|
129
|
+
shouldWriteRootAgents,
|
|
130
|
+
agentsUpdatePlan = null
|
|
131
|
+
}) {
|
|
132
|
+
const results = [];
|
|
133
|
+
const normalizedProjectionEntries = projectionEntries.length > 0
|
|
134
|
+
? projectionEntries
|
|
135
|
+
: projectionPlan.flatMap((item) => item.projectionEntries || []);
|
|
136
|
+
const normalizedManagedFiles = managedFiles && managedFiles.length > 0
|
|
137
|
+
? managedFiles
|
|
138
|
+
: projectionPlan.flatMap((item) => item.managedFiles || []);
|
|
139
|
+
const projectionMap = new Map(normalizedProjectionEntries.map((item) => [item.outputPath, item]));
|
|
140
|
+
|
|
141
|
+
for (const rel of normalizedManagedFiles) {
|
|
142
|
+
if (rel === 'AGENTS.md' && !shouldWriteRootAgents) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const entry = projectionMap.get(rel);
|
|
147
|
+
const srcPath = rel === 'AGENTS.md'
|
|
148
|
+
? srcAgents
|
|
149
|
+
: path.join(path.join(__dirname, '..', 'templates'), entry.source);
|
|
150
|
+
const existing = rel === 'AGENTS.md'
|
|
151
|
+
? { file: rel, absolutePath: path.join(cwd, rel), sourceKind: 'current' }
|
|
152
|
+
: await resolveExistingManagedPath(cwd, rel);
|
|
153
|
+
const destPath = existing.absolutePath;
|
|
154
|
+
|
|
155
|
+
const srcExists = await pathExists(srcPath);
|
|
156
|
+
const destExists = await pathExists(destPath);
|
|
157
|
+
|
|
158
|
+
if (!srcExists && !destExists) continue;
|
|
159
|
+
|
|
160
|
+
if (srcExists && !destExists) {
|
|
161
|
+
results.push({
|
|
162
|
+
file: rel,
|
|
163
|
+
type: 'added',
|
|
164
|
+
summary: [],
|
|
165
|
+
oldContent: '',
|
|
166
|
+
newContent: await readTextOrEmpty(srcPath)
|
|
167
|
+
});
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!srcExists && destExists) {
|
|
172
|
+
results.push({
|
|
173
|
+
file: rel,
|
|
174
|
+
type: 'deleted',
|
|
175
|
+
summary: [],
|
|
176
|
+
oldContent: await readTextOrEmpty(destPath),
|
|
177
|
+
newContent: ''
|
|
178
|
+
});
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const oldContent = await readTextOrEmpty(destPath);
|
|
183
|
+
let newContent = await readTextOrEmpty(srcPath);
|
|
184
|
+
|
|
185
|
+
if (rel === 'AGENTS.md' && shouldWriteRootAgents && destExists) {
|
|
186
|
+
if (agentsUpdatePlan && agentsUpdatePlan.mode === 'skip') {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
newContent = agentsUpdatePlan ? agentsUpdatePlan.content : newContent;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (normalize(oldContent) === normalize(newContent)) continue;
|
|
193
|
+
|
|
194
|
+
results.push({
|
|
195
|
+
file: rel,
|
|
196
|
+
type: 'modified',
|
|
197
|
+
summary: createLineDiff(oldContent, newContent),
|
|
198
|
+
oldContent,
|
|
199
|
+
newContent
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return results;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function groupChanges(changes) {
|
|
207
|
+
return {
|
|
208
|
+
added: changes.filter((item) => item.type === 'added'),
|
|
209
|
+
modified: changes.filter((item) => item.type === 'modified'),
|
|
210
|
+
deleted: changes.filter((item) => item.type === 'deleted')
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function printColorBlock(title, color, items, prefix) {
|
|
215
|
+
if (items.length === 0) return;
|
|
216
|
+
console.log(` ${color}${title} (${items.length})${c.reset}`);
|
|
217
|
+
for (const item of items) {
|
|
218
|
+
console.log(` ${prefix} ${item.file}`);
|
|
219
|
+
}
|
|
220
|
+
console.log('');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function printPreview({ fromVersion, toVersion, changes }) {
|
|
224
|
+
const grouped = groupChanges(changes);
|
|
225
|
+
const fromLabel = fromVersion ? `v${fromVersion}` : 'fresh-install';
|
|
226
|
+
const toLabel = `v${toVersion}`;
|
|
227
|
+
|
|
228
|
+
console.log('╔══════════════════════════════════════════════════════════════╗');
|
|
229
|
+
console.log(`║ ANWS UPDATE PREVIEW - ${fromLabel} → ${toLabel}`.padEnd(63, ' ') + '║');
|
|
230
|
+
console.log('╚══════════════════════════════════════════════════════════════╝');
|
|
231
|
+
console.log('');
|
|
232
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
233
|
+
console.log('📁 文件级变更');
|
|
234
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
235
|
+
console.log('');
|
|
236
|
+
|
|
237
|
+
printColorBlock('新增', c.green, grouped.added, '+');
|
|
238
|
+
printColorBlock('修改', c.yellow, grouped.modified, '~');
|
|
239
|
+
printColorBlock('删除', c.red, grouped.deleted, '-');
|
|
240
|
+
|
|
241
|
+
if (grouped.added.length === 0 && grouped.modified.length === 0 && grouped.deleted.length === 0) {
|
|
242
|
+
console.log(' Already up to date.');
|
|
243
|
+
console.log('');
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
248
|
+
console.log('📄 内容级变更详情');
|
|
249
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
250
|
+
console.log('');
|
|
251
|
+
|
|
252
|
+
for (const item of grouped.modified) {
|
|
253
|
+
console.log(`${c.yellow}${item.file}${c.reset}`);
|
|
254
|
+
console.log('──────────────────────────────────────────────────────────────');
|
|
255
|
+
for (const pair of item.summary) {
|
|
256
|
+
console.log(` old ${formatLineNumber(pair.oldLineNumber).padStart(4, ' ')} | ${c.red}-${c.reset} ${pair.oldText}`);
|
|
257
|
+
console.log(` new ${formatLineNumber(pair.newLineNumber).padStart(4, ' ')} | ${c.green}+${c.reset} ${pair.newText}`);
|
|
258
|
+
console.log(' ----');
|
|
259
|
+
}
|
|
260
|
+
console.log('');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
console.log('执行 `anws update` 以应用以上变更。');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module.exports = {
|
|
267
|
+
collectManagedFileDiffs,
|
|
268
|
+
groupChanges,
|
|
269
|
+
printPreview
|
|
270
|
+
};
|
package/lib/init.js
CHANGED
|
@@ -1,79 +1,123 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const fs = require('node:fs/promises');
|
|
4
3
|
const path = require('node:path');
|
|
5
|
-
const {
|
|
6
|
-
const {
|
|
4
|
+
const { buildProjectionPlan } = require('./manifest');
|
|
5
|
+
const { getTarget, listTargets } = require('./adapters');
|
|
7
6
|
const { resolveAgentsInstall, printLegacyMigrationWarning, pathExists } = require('./agents');
|
|
8
|
-
const {
|
|
7
|
+
const { ensureChangelogDir } = require('./changelog');
|
|
8
|
+
const { ROOT_AGENTS_FILE, resolveCanonicalSource } = require('./resources');
|
|
9
|
+
const { writeTargetFiles } = require('./copy');
|
|
10
|
+
const { createInstallLock, dedupeTargets, detectInstallState, summarizeTargetState, writeInstallLock } = require('./install-state');
|
|
11
|
+
const { selectMultiple, confirm } = require('./prompt');
|
|
12
|
+
const { success, warn, info, fileLine, skippedLine, blank, logo, section } = require('./output');
|
|
9
13
|
|
|
10
|
-
/**
|
|
11
|
-
* anws init — 将工作流系统写入当前项目
|
|
12
|
-
*/
|
|
13
14
|
async function init() {
|
|
14
15
|
const cwd = process.cwd();
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
logo();
|
|
17
|
+
const targets = await selectTargets();
|
|
18
|
+
const targetIds = Array.from(new Set(targets.map((item) => item.id)));
|
|
19
|
+
const targetPlans = buildProjectionPlan(targetIds);
|
|
20
|
+
const installState = await detectInstallState(cwd);
|
|
21
|
+
const srcAgents = ROOT_AGENTS_FILE;
|
|
22
|
+
const cliVersion = require(path.join(__dirname, '..', 'package.json')).version;
|
|
23
|
+
|
|
24
|
+
info('Initializing Anws...');
|
|
25
|
+
info(`Target IDEs: ${targets.map((item) => item.label).join(', ')}`);
|
|
26
|
+
if (installState.needsFallback) {
|
|
27
|
+
info('Install lock missing or unreadable. Falling back to scanned target state.');
|
|
28
|
+
}
|
|
29
|
+
if (installState.drift.hasDrift) {
|
|
30
|
+
warn(`Detected install-state drift. Missing on disk: ${installState.drift.missingOnDisk.join(', ') || 'none'}; untracked on disk: ${installState.drift.untrackedOnDisk.join(', ') || 'none'}.`);
|
|
31
|
+
}
|
|
32
|
+
blank();
|
|
24
33
|
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
const written = [];
|
|
35
|
+
const skipped = [];
|
|
36
|
+
const successfulTargets = [];
|
|
37
|
+
const failedTargets = [];
|
|
38
|
+
const sessionWrittenFiles = new Set();
|
|
39
|
+
|
|
40
|
+
for (const targetPlan of targetPlans) {
|
|
41
|
+
const target = getTarget(targetPlan.targetId);
|
|
42
|
+
const agentsDecision = target.id === 'antigravity'
|
|
43
|
+
? await resolveAgentsInstall({
|
|
44
|
+
cwd,
|
|
45
|
+
askMigrate,
|
|
46
|
+
forceYes: !!global.__ANWS_FORCE_YES
|
|
47
|
+
})
|
|
48
|
+
: {
|
|
49
|
+
shouldWriteRootAgents: true,
|
|
50
|
+
shouldWarnMigration: false
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const conflicting = await findConflicts(cwd, targetPlan.managedFiles, sessionWrittenFiles);
|
|
54
|
+
if (conflicting.length > 0) {
|
|
55
|
+
const confirmed = await askOverwrite(conflicting.length, target.label);
|
|
56
|
+
if (!confirmed) {
|
|
57
|
+
skipped.push(...targetPlan.managedFiles);
|
|
58
|
+
failedTargets.push({
|
|
59
|
+
targetId: target.id,
|
|
60
|
+
targetLabel: target.label,
|
|
61
|
+
reason: `Skipped ${conflicting.length} conflicting managed file(s)`
|
|
62
|
+
});
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
33
65
|
}
|
|
34
|
-
|
|
35
|
-
const
|
|
66
|
+
|
|
67
|
+
const result = await writeTargetFiles(cwd, {
|
|
68
|
+
targetPlan,
|
|
69
|
+
protectedFiles: targetPlan.userProtectedFiles,
|
|
36
70
|
srcAgents,
|
|
37
|
-
shouldWriteRootAgents: agentsDecision.shouldWriteRootAgents
|
|
71
|
+
shouldWriteRootAgents: agentsDecision.shouldWriteRootAgents,
|
|
72
|
+
resolveCanonicalSource
|
|
38
73
|
});
|
|
74
|
+
|
|
75
|
+
written.push(...result.written);
|
|
76
|
+
for (const rel of result.written) {
|
|
77
|
+
sessionWrittenFiles.add(rel);
|
|
78
|
+
}
|
|
79
|
+
skipped.push(...result.skipped);
|
|
80
|
+
successfulTargets.push(summarizeTargetState(targetPlan, cliVersion));
|
|
81
|
+
|
|
39
82
|
if (agentsDecision.shouldWarnMigration) {
|
|
40
83
|
printLegacyMigrationWarning();
|
|
41
84
|
}
|
|
42
|
-
printSummary(updated, skipped, 'updated');
|
|
43
|
-
return;
|
|
44
85
|
}
|
|
45
|
-
// ── 无冲突:直接复制 ─────────────────────────────────────────────────────────
|
|
46
|
-
|
|
47
|
-
logo();
|
|
48
|
-
info('Initializing Antigravity Workflow System...');
|
|
49
|
-
blank();
|
|
50
86
|
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
87
|
+
await ensureChangelogDir(cwd);
|
|
88
|
+
const existingTargets = installState.lockResult.lock?.targets || [];
|
|
89
|
+
const generatedAt = new Date().toISOString();
|
|
90
|
+
await writeInstallLock(cwd, createInstallLock({
|
|
91
|
+
cliVersion,
|
|
92
|
+
generatedAt,
|
|
93
|
+
targets: dedupeTargets([
|
|
94
|
+
...existingTargets,
|
|
95
|
+
...successfulTargets
|
|
96
|
+
]),
|
|
97
|
+
lastUpdateSummary: {
|
|
98
|
+
successfulTargets: successfulTargets.map((item) => item.targetId),
|
|
99
|
+
failedTargets: failedTargets.map((item) => item.targetId),
|
|
100
|
+
updatedAt: generatedAt
|
|
61
101
|
}
|
|
62
|
-
}
|
|
102
|
+
}));
|
|
63
103
|
|
|
64
|
-
|
|
65
|
-
|
|
104
|
+
printTargetSummary(successfulTargets, failedTargets);
|
|
105
|
+
|
|
106
|
+
for (const rel of written) {
|
|
107
|
+
fileLine(rel.replace(/\\/g, '/'));
|
|
66
108
|
}
|
|
67
109
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
110
|
+
if (skipped.length > 0) {
|
|
111
|
+
blank();
|
|
112
|
+
info('Skipped (project-specific, preserved):');
|
|
113
|
+
for (const rel of skipped) {
|
|
114
|
+
skippedLine(rel.replace(/\\/g, '/'));
|
|
115
|
+
}
|
|
72
116
|
}
|
|
73
117
|
|
|
74
118
|
blank();
|
|
75
|
-
success(`Done! ${written.length} files written
|
|
76
|
-
printNextSteps();
|
|
119
|
+
success(`Done! ${written.length} files written for ${successfulTargets.map((item) => item.targetLabel).join(', ') || 'selected targets'}.`);
|
|
120
|
+
printNextSteps(targets);
|
|
77
121
|
}
|
|
78
122
|
|
|
79
123
|
// ─── 辅助函数 ──────────────────────────────────────────────────────────────────
|
|
@@ -82,9 +126,12 @@ async function init() {
|
|
|
82
126
|
* 找出 cwd 中已存在的托管文件列表。
|
|
83
127
|
* @returns {Promise<string[]>} 已存在的托管文件相对路径数组
|
|
84
128
|
*/
|
|
85
|
-
async function findConflicts(cwd) {
|
|
129
|
+
async function findConflicts(cwd, managedFiles, sessionWrittenFiles = new Set()) {
|
|
86
130
|
const conflicts = [];
|
|
87
|
-
for (const rel of
|
|
131
|
+
for (const rel of managedFiles) {
|
|
132
|
+
if (sessionWrittenFiles.has(rel)) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
88
135
|
const abs = path.join(cwd, rel);
|
|
89
136
|
const exists = await pathExists(abs);
|
|
90
137
|
if (exists) conflicts.push(rel);
|
|
@@ -97,26 +144,19 @@ async function findConflicts(cwd) {
|
|
|
97
144
|
* 非 TTY 环境(如 CI)自动返回 false。
|
|
98
145
|
* @returns {Promise<boolean>}
|
|
99
146
|
*/
|
|
100
|
-
async function askOverwrite(count) {
|
|
147
|
+
async function askOverwrite(count, label) {
|
|
101
148
|
if (global.__ANWS_FORCE_YES) return true;
|
|
102
149
|
|
|
103
|
-
// 非 TTY 环境:默认不覆盖,防止 CI 挂起
|
|
104
150
|
if (!process.stdin.isTTY) {
|
|
105
151
|
warn(`${count} managed file(s) already exist. Non-TTY: skipping overwrite.`);
|
|
106
152
|
return false;
|
|
107
153
|
}
|
|
108
154
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
`\n\u26a0 ${count} managed file(s) already exist. Overwrite? [y/N] `,
|
|
115
|
-
(answer) => {
|
|
116
|
-
rl.close();
|
|
117
|
-
resolve(answer.trim().toLowerCase() === 'y');
|
|
118
|
-
}
|
|
119
|
-
);
|
|
155
|
+
return confirm({
|
|
156
|
+
message: `Overwrite ${count} managed file(s) for ${label}?`,
|
|
157
|
+
confirmLabel: 'Overwrite',
|
|
158
|
+
cancelLabel: 'Skip',
|
|
159
|
+
defaultValue: false
|
|
120
160
|
});
|
|
121
161
|
}
|
|
122
162
|
|
|
@@ -127,62 +167,14 @@ async function askMigrate() {
|
|
|
127
167
|
return false;
|
|
128
168
|
}
|
|
129
169
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
'\n\u26a0 Legacy .agent/rules/agents.md detected. Do you want to migrate to root AGENTS.md? [y/N] ',
|
|
136
|
-
(answer) => {
|
|
137
|
-
rl.close();
|
|
138
|
-
resolve(answer.trim().toLowerCase() === 'y');
|
|
139
|
-
}
|
|
140
|
-
);
|
|
170
|
+
return confirm({
|
|
171
|
+
message: 'Legacy .agent/ directory detected. Migrate to .agents/?',
|
|
172
|
+
confirmLabel: 'Migrate',
|
|
173
|
+
cancelLabel: 'Keep legacy',
|
|
174
|
+
defaultValue: false
|
|
141
175
|
});
|
|
142
176
|
}
|
|
143
177
|
|
|
144
|
-
/**
|
|
145
|
-
* 仅覆盖 MANAGED_FILES 清单内的文件,用户自有文件不受影响。
|
|
146
|
-
* USER_PROTECTED_FILES 中的文件即便冲突也跳过,保留用户修改。
|
|
147
|
-
* @returns {{ written: string[], skipped: string[] }}
|
|
148
|
-
*/
|
|
149
|
-
async function overwriteManaged(srcRoot, cwd, options = {}) {
|
|
150
|
-
const srcBase = path.dirname(srcRoot); // templates/
|
|
151
|
-
const written = [];
|
|
152
|
-
const skipped = [];
|
|
153
|
-
const shouldWriteRootAgents = options.shouldWriteRootAgents !== false;
|
|
154
|
-
const srcAgents = options.srcAgents || path.join(srcBase, 'AGENTS.md');
|
|
155
|
-
|
|
156
|
-
for (const rel of MANAGED_FILES) {
|
|
157
|
-
if (rel === 'AGENTS.md' && !shouldWriteRootAgents) {
|
|
158
|
-
skipped.push(rel);
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// 受保护文件:文件已存在时跳过,交给用户自行维护
|
|
163
|
-
if (USER_PROTECTED_FILES.includes(rel)) {
|
|
164
|
-
const destPath = path.join(cwd, rel);
|
|
165
|
-
const exists = await pathExists(destPath);
|
|
166
|
-
if (exists) {
|
|
167
|
-
skipped.push(rel);
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const srcPath = rel === 'AGENTS.md' ? srcAgents : path.join(srcBase, rel);
|
|
173
|
-
const destPath = path.join(cwd, rel);
|
|
174
|
-
|
|
175
|
-
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
|
176
|
-
const srcExists = await pathExists(srcPath);
|
|
177
|
-
if (srcExists) {
|
|
178
|
-
await fs.copyFile(srcPath, destPath);
|
|
179
|
-
written.push(rel);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return { written, skipped };
|
|
184
|
-
}
|
|
185
|
-
|
|
186
178
|
/**
|
|
187
179
|
* 打印操作摘要(更新场景)。
|
|
188
180
|
* @param {string[]} files 已写入的文件
|
|
@@ -211,11 +203,44 @@ function printSummary(files, skipped = [], action) {
|
|
|
211
203
|
}
|
|
212
204
|
}
|
|
213
205
|
|
|
214
|
-
function
|
|
206
|
+
async function selectTargets() {
|
|
207
|
+
if (global.__ANWS_TARGET_IDS && global.__ANWS_TARGET_IDS.length > 0) {
|
|
208
|
+
return global.__ANWS_TARGET_IDS.map((targetId) => getTarget(targetId));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!process.stdin.isTTY) {
|
|
212
|
+
return [getTarget('antigravity')];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const targets = listTargets();
|
|
216
|
+
|
|
217
|
+
return selectMultiple({
|
|
218
|
+
message: 'Choose your target AI IDEs:',
|
|
219
|
+
options: targets.map((target) => ({ label: target.label, value: target.id })),
|
|
220
|
+
initialSelectedIndexes: [1]
|
|
221
|
+
}).then((selectedOptions) => selectedOptions.map((option) => getTarget(option.value)));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function printNextSteps(targets) {
|
|
215
225
|
blank();
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
226
|
+
section('Next steps', targets.some((target) => target.rootAgentFile)
|
|
227
|
+
? [
|
|
228
|
+
'1. Read AGENTS.md to understand the system',
|
|
229
|
+
'2. Run /quickstart in your AI assistant to analyze and start the workflow'
|
|
230
|
+
]
|
|
231
|
+
: [
|
|
232
|
+
'1. Review files written under the selected target directories',
|
|
233
|
+
'2. Run /quickstart in your AI assistant to analyze and start the workflow'
|
|
234
|
+
]);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function printTargetSummary(successfulTargets, failedTargets) {
|
|
238
|
+
blank();
|
|
239
|
+
section('Target summary', [
|
|
240
|
+
...successfulTargets.map((target) => `✔ ${target.targetLabel} (${target.targetId})`),
|
|
241
|
+
...failedTargets.map((target) => `✖ ${target.targetLabel} (${target.targetId}) — ${target.reason}`)
|
|
242
|
+
]);
|
|
219
243
|
}
|
|
220
244
|
|
|
221
245
|
module.exports = init;
|
|
246
|
+
|