@haaaiawd/anws 1.2.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +208 -173
- package/bin/cli.js +22 -9
- package/lib/adapters/index.js +157 -0
- package/lib/agents.js +185 -0
- package/lib/changelog.js +187 -0
- package/lib/copy.js +72 -1
- package/lib/diff.js +270 -0
- package/lib/init.js +238 -174
- 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 +355 -149
- 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 +166 -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 +38 -33
- package/templates/{.agent → .agents}/workflows/challenge.md +21 -15
- package/templates/{.agent → .agents}/workflows/change.md +24 -15
- package/templates/{.agent → .agents}/workflows/craft.md +9 -20
- package/templates/{.agent → .agents}/workflows/design-system.md +83 -56
- package/templates/{.agent → .agents}/workflows/explore.md +6 -19
- package/templates/{.agent → .agents}/workflows/forge.md +36 -38
- package/templates/{.agent → .agents}/workflows/genesis.md +76 -64
- 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 +134 -113
- 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,175 +1,239 @@
|
|
|
1
|
-
'use strict';
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { buildProjectionPlan } = require('./manifest');
|
|
5
|
+
const { getTarget, listTargets } = require('./adapters');
|
|
6
|
+
const { resolveAgentsInstall, printLegacyMigrationWarning, pathExists } = require('./agents');
|
|
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');
|
|
13
|
+
|
|
14
|
+
async function init() {
|
|
15
|
+
const cwd = process.cwd();
|
|
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();
|
|
33
|
+
|
|
34
|
+
const written = [];
|
|
35
|
+
const skipped = [];
|
|
36
|
+
const successfulTargets = [];
|
|
37
|
+
const failedTargets = [];
|
|
38
|
+
|
|
39
|
+
for (const targetPlan of targetPlans) {
|
|
40
|
+
const target = getTarget(targetPlan.targetId);
|
|
41
|
+
const agentsDecision = target.id === 'antigravity'
|
|
42
|
+
? await resolveAgentsInstall({
|
|
43
|
+
cwd,
|
|
44
|
+
askMigrate,
|
|
45
|
+
forceYes: !!global.__ANWS_FORCE_YES
|
|
46
|
+
})
|
|
47
|
+
: {
|
|
48
|
+
shouldWriteRootAgents: false,
|
|
49
|
+
shouldWarnMigration: false
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const conflicting = await findConflicts(cwd, targetPlan.managedFiles);
|
|
53
|
+
if (conflicting.length > 0) {
|
|
54
|
+
const confirmed = await askOverwrite(conflicting.length, target.label);
|
|
55
|
+
if (!confirmed) {
|
|
56
|
+
skipped.push(...targetPlan.managedFiles);
|
|
57
|
+
failedTargets.push({
|
|
58
|
+
targetId: target.id,
|
|
59
|
+
targetLabel: target.label,
|
|
60
|
+
reason: `Skipped ${conflicting.length} conflicting managed file(s)`
|
|
61
|
+
});
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const result = await writeTargetFiles(cwd, {
|
|
67
|
+
targetPlan,
|
|
68
|
+
protectedFiles: targetPlan.userProtectedFiles,
|
|
69
|
+
srcAgents,
|
|
70
|
+
shouldWriteRootAgents: agentsDecision.shouldWriteRootAgents,
|
|
71
|
+
resolveCanonicalSource
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
written.push(...result.written);
|
|
75
|
+
skipped.push(...result.skipped);
|
|
76
|
+
successfulTargets.push(summarizeTargetState(targetPlan, cliVersion));
|
|
77
|
+
|
|
78
|
+
if (agentsDecision.shouldWarnMigration) {
|
|
79
|
+
printLegacyMigrationWarning();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await ensureChangelogDir(cwd);
|
|
84
|
+
const existingTargets = installState.lockResult.lock?.targets || [];
|
|
85
|
+
const generatedAt = new Date().toISOString();
|
|
86
|
+
await writeInstallLock(cwd, createInstallLock({
|
|
87
|
+
cliVersion,
|
|
88
|
+
generatedAt,
|
|
89
|
+
targets: dedupeTargets([
|
|
90
|
+
...existingTargets,
|
|
91
|
+
...successfulTargets
|
|
92
|
+
]),
|
|
93
|
+
lastUpdateSummary: {
|
|
94
|
+
successfulTargets: successfulTargets.map((item) => item.targetId),
|
|
95
|
+
failedTargets: failedTargets.map((item) => item.targetId),
|
|
96
|
+
updatedAt: generatedAt
|
|
97
|
+
}
|
|
98
|
+
}));
|
|
99
|
+
|
|
100
|
+
printTargetSummary(successfulTargets, failedTargets);
|
|
101
|
+
|
|
102
|
+
for (const rel of written) {
|
|
103
|
+
fileLine(rel.replace(/\\/g, '/'));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (skipped.length > 0) {
|
|
107
|
+
blank();
|
|
108
|
+
info('Skipped (project-specific, preserved):');
|
|
109
|
+
for (const rel of skipped) {
|
|
110
|
+
skippedLine(rel.replace(/\\/g, '/'));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
blank();
|
|
115
|
+
success(`Done! ${written.length} files written for ${successfulTargets.map((item) => item.targetLabel).join(', ') || 'selected targets'}.`);
|
|
116
|
+
printNextSteps(targets);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ─── 辅助函数 ──────────────────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 找出 cwd 中已存在的托管文件列表。
|
|
123
|
+
* @returns {Promise<string[]>} 已存在的托管文件相对路径数组
|
|
124
|
+
*/
|
|
125
|
+
async function findConflicts(cwd, managedFiles) {
|
|
126
|
+
const conflicts = [];
|
|
127
|
+
for (const rel of managedFiles) {
|
|
128
|
+
const abs = path.join(cwd, rel);
|
|
129
|
+
const exists = await pathExists(abs);
|
|
130
|
+
if (exists) conflicts.push(rel);
|
|
131
|
+
}
|
|
132
|
+
return conflicts;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 交互式询问用户是否覆盖(默认 N)。
|
|
137
|
+
* 非 TTY 环境(如 CI)自动返回 false。
|
|
138
|
+
* @returns {Promise<boolean>}
|
|
139
|
+
*/
|
|
140
|
+
async function askOverwrite(count, label) {
|
|
141
|
+
if (global.__ANWS_FORCE_YES) return true;
|
|
142
|
+
|
|
143
|
+
if (!process.stdin.isTTY) {
|
|
144
|
+
warn(`${count} managed file(s) already exist. Non-TTY: skipping overwrite.`);
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return confirm({
|
|
149
|
+
message: `Overwrite ${count} managed file(s) for ${label}?`,
|
|
150
|
+
confirmLabel: 'Overwrite',
|
|
151
|
+
cancelLabel: 'Skip',
|
|
152
|
+
defaultValue: false
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function askMigrate() {
|
|
157
|
+
if (global.__ANWS_FORCE_YES) return true;
|
|
158
|
+
|
|
159
|
+
if (!process.stdin.isTTY) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return confirm({
|
|
164
|
+
message: 'Legacy .agent/ directory detected. Migrate to .agents/?',
|
|
165
|
+
confirmLabel: 'Migrate',
|
|
166
|
+
cancelLabel: 'Keep legacy',
|
|
167
|
+
defaultValue: false
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* 打印操作摘要(更新场景)。
|
|
173
|
+
* @param {string[]} files 已写入的文件
|
|
174
|
+
* @param {string[]} skipped 受保护跳过的文件
|
|
175
|
+
* @param {string} action
|
|
176
|
+
*/
|
|
177
|
+
function printSummary(files, skipped = [], action) {
|
|
178
|
+
const verb = action === 'updated' ? 'Updating' : 'Writing';
|
|
179
|
+
blank();
|
|
180
|
+
info(`${verb} files...`);
|
|
181
|
+
blank();
|
|
182
|
+
for (const rel of files) {
|
|
183
|
+
fileLine(rel.replace(/\\/g, '/'));
|
|
184
|
+
}
|
|
185
|
+
if (skipped.length > 0) {
|
|
186
|
+
blank();
|
|
187
|
+
info('Skipped (project-specific, preserved):');
|
|
188
|
+
for (const rel of skipped) {
|
|
189
|
+
skippedLine(rel.replace(/\\/g, '/'));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
blank();
|
|
193
|
+
success(`Done! ${files.length} file(s) ${action}${skipped.length > 0 ? `, ${skipped.length} skipped` : ''}.`);
|
|
194
|
+
if (action === 'updated') {
|
|
195
|
+
info('Managed files have been updated to the latest version.');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function selectTargets() {
|
|
200
|
+
if (global.__ANWS_TARGET_IDS && global.__ANWS_TARGET_IDS.length > 0) {
|
|
201
|
+
return global.__ANWS_TARGET_IDS.map((targetId) => getTarget(targetId));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (!process.stdin.isTTY) {
|
|
205
|
+
return [getTarget('antigravity')];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const targets = listTargets();
|
|
209
|
+
|
|
210
|
+
return selectMultiple({
|
|
211
|
+
message: 'Choose your target AI IDEs:',
|
|
212
|
+
options: targets.map((target) => ({ label: target.label, value: target.id })),
|
|
213
|
+
initialSelectedIndexes: [1]
|
|
214
|
+
}).then((selectedOptions) => selectedOptions.map((option) => getTarget(option.value)));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function printNextSteps(targets) {
|
|
218
|
+
blank();
|
|
219
|
+
section('Next steps', targets.some((target) => target.rootAgentFile)
|
|
220
|
+
? [
|
|
221
|
+
'1. Read AGENTS.md to understand the system',
|
|
222
|
+
'2. Run /quickstart in your AI assistant to analyze and start the workflow'
|
|
223
|
+
]
|
|
224
|
+
: [
|
|
225
|
+
'1. Review files written under the selected target directories',
|
|
226
|
+
'2. Run /quickstart in your AI assistant to analyze and start the workflow'
|
|
227
|
+
]);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function printTargetSummary(successfulTargets, failedTargets) {
|
|
231
|
+
blank();
|
|
232
|
+
section('Target summary', [
|
|
233
|
+
...successfulTargets.map((target) => `✔ ${target.targetLabel} (${target.targetId})`),
|
|
234
|
+
...failedTargets.map((target) => `✖ ${target.targetLabel} (${target.targetId}) — ${target.reason}`)
|
|
235
|
+
]);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
module.exports = init;
|
|
2
239
|
|
|
3
|
-
const fs = require('node:fs/promises');
|
|
4
|
-
const path = require('node:path');
|
|
5
|
-
const { copyDir } = require('./copy');
|
|
6
|
-
const { MANAGED_FILES, USER_PROTECTED_FILES } = require('./manifest');
|
|
7
|
-
const { success, warn, info, fileLine, skippedLine, blank, logo } = require('./output');
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* anws init — 将工作流系统写入当前项目
|
|
11
|
-
*/
|
|
12
|
-
async function init() {
|
|
13
|
-
const cwd = process.cwd();
|
|
14
|
-
const srcRoot = path.join(__dirname, '..', 'templates', '.agent');
|
|
15
|
-
const destRoot = path.join(cwd, '.agent');
|
|
16
|
-
|
|
17
|
-
// ── 冲突检测(T1.2.3 在此处插入冲突分支)──────────────────────────────────
|
|
18
|
-
const conflicting = await findConflicts(cwd);
|
|
19
|
-
if (conflicting.length > 0) {
|
|
20
|
-
const confirmed = await askOverwrite(conflicting.length);
|
|
21
|
-
if (!confirmed) {
|
|
22
|
-
blank();
|
|
23
|
-
info('Aborted. No files were changed.');
|
|
24
|
-
process.exit(0);
|
|
25
|
-
}
|
|
26
|
-
// 仅覆盖托管文件(用户自有文件不受影响)
|
|
27
|
-
const { written: updated, skipped } = await overwriteManaged(srcRoot, cwd);
|
|
28
|
-
printSummary(updated, skipped, 'updated');
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
// ── 无冲突:直接复制 ─────────────────────────────────────────────────────────
|
|
32
|
-
|
|
33
|
-
logo();
|
|
34
|
-
info('Initializing Antigravity Workflow System...');
|
|
35
|
-
blank();
|
|
36
|
-
|
|
37
|
-
const writtenFiles = await copyDir(srcRoot, destRoot);
|
|
38
|
-
const written = Array.isArray(writtenFiles) ? writtenFiles : [];
|
|
39
|
-
|
|
40
|
-
// 把外层的 AGENTS.md 拷贝出来
|
|
41
|
-
const srcAgents = path.join(__dirname, '..', 'templates', 'AGENTS.md');
|
|
42
|
-
const destAgents = path.join(cwd, 'AGENTS.md');
|
|
43
|
-
try {
|
|
44
|
-
await fs.copyFile(srcAgents, destAgents);
|
|
45
|
-
written.push(destAgents);
|
|
46
|
-
} catch (e) {
|
|
47
|
-
// 忽略
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// 打印文件列表
|
|
51
|
-
for (const absPath of written) {
|
|
52
|
-
const rel = path.relative(cwd, absPath).replace(/\\/g, '/');
|
|
53
|
-
fileLine(rel);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
blank();
|
|
57
|
-
success(`Done! ${written.length} files written to .agent/`);
|
|
58
|
-
printNextSteps();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// ─── 辅助函数 ──────────────────────────────────────────────────────────────────
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* 找出 cwd 中已存在的托管文件列表。
|
|
65
|
-
* @returns {Promise<string[]>} 已存在的托管文件相对路径数组
|
|
66
|
-
*/
|
|
67
|
-
async function findConflicts(cwd) {
|
|
68
|
-
const conflicts = [];
|
|
69
|
-
for (const rel of MANAGED_FILES) {
|
|
70
|
-
const abs = path.join(cwd, rel);
|
|
71
|
-
const exists = await fs.access(abs).then(() => true).catch(() => false);
|
|
72
|
-
if (exists) conflicts.push(rel);
|
|
73
|
-
}
|
|
74
|
-
return conflicts;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* 交互式询问用户是否覆盖(默认 N)。
|
|
79
|
-
* 非 TTY 环境(如 CI)自动返回 false。
|
|
80
|
-
* @returns {Promise<boolean>}
|
|
81
|
-
*/
|
|
82
|
-
async function askOverwrite(count) {
|
|
83
|
-
if (global.__ANWS_FORCE_YES) return true;
|
|
84
|
-
|
|
85
|
-
// 非 TTY 环境:默认不覆盖,防止 CI 挂起
|
|
86
|
-
if (!process.stdin.isTTY) {
|
|
87
|
-
warn(`${count} managed file(s) already exist. Non-TTY: skipping overwrite.`);
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const readline = require('node:readline');
|
|
92
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
93
|
-
|
|
94
|
-
return new Promise((resolve) => {
|
|
95
|
-
rl.question(
|
|
96
|
-
`\n\u26a0 ${count} managed file(s) already exist. Overwrite? [y/N] `,
|
|
97
|
-
(answer) => {
|
|
98
|
-
rl.close();
|
|
99
|
-
resolve(answer.trim().toLowerCase() === 'y');
|
|
100
|
-
}
|
|
101
|
-
);
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* 仅覆盖 MANAGED_FILES 清单内的文件,用户自有文件不受影响。
|
|
107
|
-
* USER_PROTECTED_FILES 中的文件即便冲突也跳过,保留用户修改。
|
|
108
|
-
* @returns {{ written: string[], skipped: string[] }}
|
|
109
|
-
*/
|
|
110
|
-
async function overwriteManaged(srcRoot, cwd) {
|
|
111
|
-
const srcBase = path.dirname(srcRoot); // templates/
|
|
112
|
-
const written = [];
|
|
113
|
-
const skipped = [];
|
|
114
|
-
|
|
115
|
-
for (const rel of MANAGED_FILES) {
|
|
116
|
-
// 受保护文件:文件已存在时跳过,交给用户自行维护
|
|
117
|
-
if (USER_PROTECTED_FILES.includes(rel)) {
|
|
118
|
-
const destPath = path.join(cwd, rel);
|
|
119
|
-
const exists = await fs.access(destPath).then(() => true).catch(() => false);
|
|
120
|
-
if (exists) {
|
|
121
|
-
skipped.push(rel);
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const srcPath = path.join(srcBase, rel);
|
|
127
|
-
const destPath = path.join(cwd, rel);
|
|
128
|
-
|
|
129
|
-
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
|
130
|
-
const srcExists = await fs.access(srcPath).then(() => true).catch(() => false);
|
|
131
|
-
if (srcExists) {
|
|
132
|
-
await fs.copyFile(srcPath, destPath);
|
|
133
|
-
written.push(rel);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return { written, skipped };
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* 打印操作摘要(更新场景)。
|
|
142
|
-
* @param {string[]} files 已写入的文件
|
|
143
|
-
* @param {string[]} skipped 受保护跳过的文件
|
|
144
|
-
* @param {string} action
|
|
145
|
-
*/
|
|
146
|
-
function printSummary(files, skipped = [], action) {
|
|
147
|
-
const verb = action === 'updated' ? 'Updating' : 'Writing';
|
|
148
|
-
blank();
|
|
149
|
-
info(`${verb} files...`);
|
|
150
|
-
blank();
|
|
151
|
-
for (const rel of files) {
|
|
152
|
-
fileLine(rel.replace(/\\/g, '/'));
|
|
153
|
-
}
|
|
154
|
-
if (skipped.length > 0) {
|
|
155
|
-
blank();
|
|
156
|
-
info('Skipped (project-specific, preserved):');
|
|
157
|
-
for (const rel of skipped) {
|
|
158
|
-
skippedLine(rel.replace(/\\/g, '/'));
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
blank();
|
|
162
|
-
success(`Done! ${files.length} file(s) ${action}${skipped.length > 0 ? `, ${skipped.length} skipped` : ''}.`);
|
|
163
|
-
if (action === 'updated') {
|
|
164
|
-
info('Managed files have been updated to the latest version.');
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function printNextSteps() {
|
|
169
|
-
blank();
|
|
170
|
-
info('Next steps:');
|
|
171
|
-
info(' 1. Read AGENTS.md to understand the system');
|
|
172
|
-
info(' 2. Run /quickstart in your AI assistant to analyze and start the workflow');
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
module.exports = init;
|