@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.
Files changed (71) hide show
  1. package/README.md +230 -174
  2. package/bin/cli.js +22 -9
  3. package/lib/adapters/index.js +157 -0
  4. package/lib/agents.js +136 -1
  5. package/lib/changelog.js +187 -0
  6. package/lib/copy.js +72 -1
  7. package/lib/diff.js +270 -0
  8. package/lib/init.js +150 -125
  9. package/lib/install-state.js +195 -0
  10. package/lib/manifest.js +184 -42
  11. package/lib/output.js +185 -13
  12. package/lib/prompt.js +284 -0
  13. package/lib/resources/index.js +27 -0
  14. package/lib/update.js +291 -83
  15. package/package.json +10 -6
  16. package/templates/.agents/skills/concept-modeler/SKILL.md +176 -0
  17. package/templates/{.agent → .agents}/skills/design-reviewer/SKILL.md +6 -6
  18. package/templates/.agents/skills/nexus-mapper/SKILL.md +306 -0
  19. package/templates/.agents/skills/nexus-mapper/references/language-customization.md +164 -0
  20. package/templates/.agents/skills/nexus-mapper/references/output-schema.md +298 -0
  21. package/templates/.agents/skills/nexus-mapper/references/probe-protocol.md +246 -0
  22. package/templates/.agents/skills/nexus-mapper/scripts/extract_ast.py +706 -0
  23. package/templates/.agents/skills/nexus-mapper/scripts/git_detective.py +194 -0
  24. package/templates/.agents/skills/nexus-mapper/scripts/languages.json +127 -0
  25. package/templates/.agents/skills/nexus-mapper/scripts/query_graph.py +556 -0
  26. package/templates/.agents/skills/nexus-mapper/scripts/requirements.txt +6 -0
  27. package/templates/{.agent → .agents}/skills/report-template/SKILL.md +11 -14
  28. package/templates/.agents/skills/report-template/references/REPORT_TEMPLATE.md +100 -0
  29. package/templates/{.agent → .agents}/skills/runtime-inspector/SKILL.md +1 -1
  30. package/templates/.agents/skills/sequential-thinking/SKILL.md +179 -0
  31. package/templates/.agents/skills/spec-writer/SKILL.md +108 -0
  32. package/templates/{.agent → .agents}/skills/spec-writer/references/prd_template.md +1 -1
  33. package/templates/{.agent → .agents}/skills/system-architect/SKILL.md +3 -3
  34. package/templates/.agents/skills/system-architect/references/rfc_template.md +59 -0
  35. package/templates/{.agent → .agents}/skills/system-designer/SKILL.md +6 -6
  36. package/templates/{.agent → .agents}/skills/system-designer/references/system-design-template.md +75 -25
  37. package/templates/{.agent → .agents}/skills/task-planner/SKILL.md +1 -1
  38. package/templates/.agents/skills/task-planner/references/TASK_TEMPLATE.md +144 -0
  39. package/templates/{.agent → .agents}/skills/task-reviewer/SKILL.md +4 -3
  40. package/templates/{.agent → .agents}/skills/tech-evaluator/SKILL.md +2 -2
  41. package/templates/{.agent → .agents}/skills/tech-evaluator/references/ADR_TEMPLATE.md +10 -0
  42. package/templates/{.agent → .agents}/workflows/blueprint.md +32 -27
  43. package/templates/{.agent → .agents}/workflows/challenge.md +21 -15
  44. package/templates/{.agent → .agents}/workflows/change.md +23 -14
  45. package/templates/{.agent → .agents}/workflows/craft.md +8 -19
  46. package/templates/{.agent → .agents}/workflows/design-system.md +81 -54
  47. package/templates/{.agent → .agents}/workflows/explore.md +6 -19
  48. package/templates/{.agent → .agents}/workflows/forge.md +30 -32
  49. package/templates/{.agent → .agents}/workflows/genesis.md +68 -56
  50. package/templates/.agents/workflows/probe.md +168 -0
  51. package/templates/{.agent → .agents}/workflows/quickstart.md +7 -12
  52. package/templates/.agents/workflows/upgrade.md +192 -0
  53. package/templates/AGENTS.md +66 -45
  54. package/templates/.agent/skills/build-inspector/SKILL.md +0 -83
  55. package/templates/.agent/skills/complexity-guard/SKILL.md +0 -71
  56. package/templates/.agent/skills/complexity-guard/references/anti_patterns.md +0 -21
  57. package/templates/.agent/skills/concept-modeler/SKILL.md +0 -112
  58. package/templates/.agent/skills/concept-modeler/prompts/GLOSSARY_PROMPT.md +0 -40
  59. package/templates/.agent/skills/concept-modeler/references/ENTITY_EXTRACTION_PROMPT.md +0 -299
  60. package/templates/.agent/skills/concept-modeler/scripts/glossary_gen.py +0 -66
  61. package/templates/.agent/skills/git-forensics/SKILL.md +0 -74
  62. package/templates/.agent/skills/git-forensics/references/ANALYSIS_METHODOLOGY.md +0 -193
  63. package/templates/.agent/skills/git-forensics/scripts/__pycache__/git_forensics.cpython-313.pyc +0 -0
  64. package/templates/.agent/skills/git-forensics/scripts/git_forensics.py +0 -615
  65. package/templates/.agent/skills/git-forensics/scripts/git_hotspots.py +0 -118
  66. package/templates/.agent/skills/report-template/references/REPORT_TEMPLATE.md +0 -100
  67. package/templates/.agent/skills/spec-writer/SKILL.md +0 -108
  68. package/templates/.agent/skills/system-architect/references/rfc_template.md +0 -59
  69. package/templates/.agent/skills/task-planner/references/TASK_TEMPLATE.md +0 -144
  70. package/templates/.agent/workflows/scout.md +0 -139
  71. /package/templates/{.agent → .agents}/skills/system-designer/references/system-design-detail-template.md +0 -0
@@ -0,0 +1,195 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { detectInstalledTargets } = require('./adapters');
6
+
7
+ const INSTALL_LOCK_RELATIVE_PATH = '.anws/install-lock.json';
8
+ const INSTALL_LOCK_VERSION = 1;
9
+
10
+ function getInstallLockPath(cwd) {
11
+ return path.join(cwd, INSTALL_LOCK_RELATIVE_PATH);
12
+ }
13
+
14
+ function ensureString(value, fieldName) {
15
+ if (typeof value !== 'string' || value.trim() === '') {
16
+ throw new Error(`Invalid install-lock: ${fieldName} must be a non-empty string`);
17
+ }
18
+ return value;
19
+ }
20
+
21
+ function ensureObject(value, fieldName) {
22
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
23
+ throw new Error(`Invalid install-lock: ${fieldName} must be an object`);
24
+ }
25
+ return value;
26
+ }
27
+
28
+ function ensureArray(value, fieldName) {
29
+ if (!Array.isArray(value)) {
30
+ throw new Error(`Invalid install-lock: ${fieldName} must be an array`);
31
+ }
32
+ return value;
33
+ }
34
+
35
+ function normalizeTargetInstallation(target) {
36
+ const normalized = ensureObject(target, 'targets[]');
37
+ const targetId = ensureString(normalized.targetId, 'targets[].targetId');
38
+ const targetLabel = ensureString(normalized.targetLabel, 'targets[].targetLabel');
39
+ const installedVersion = ensureString(normalized.installedVersion, 'targets[].installedVersion');
40
+ const managedFiles = Array.from(new Set(ensureArray(normalized.managedFiles || [], 'targets[].managedFiles')));
41
+ const ownership = Array.from(new Set(ensureArray(normalized.ownership || [], 'targets[].ownership')));
42
+
43
+ return {
44
+ targetId,
45
+ targetLabel,
46
+ installedVersion,
47
+ managedFiles,
48
+ ownership,
49
+ lastSuccessfulUpdate: normalized.lastSuccessfulUpdate && typeof normalized.lastSuccessfulUpdate === 'object'
50
+ ? {
51
+ version: ensureString(normalized.lastSuccessfulUpdate.version, 'targets[].lastSuccessfulUpdate.version'),
52
+ updatedAt: ensureString(normalized.lastSuccessfulUpdate.updatedAt, 'targets[].lastSuccessfulUpdate.updatedAt')
53
+ }
54
+ : null
55
+ };
56
+ }
57
+
58
+ function dedupeTargets(targets) {
59
+ const map = new Map();
60
+ for (const target of targets.map(normalizeTargetInstallation)) {
61
+ map.set(target.targetId, target);
62
+ }
63
+ return Array.from(map.values()).sort((a, b) => a.targetId.localeCompare(b.targetId));
64
+ }
65
+
66
+ function normalizeInstallLock(input) {
67
+ const source = ensureObject(input, 'install-lock');
68
+ const schemaVersion = source.schemaVersion ?? source.lockVersion ?? INSTALL_LOCK_VERSION;
69
+ if (!Number.isInteger(schemaVersion) || schemaVersion < 1) {
70
+ throw new Error('Invalid install-lock: schemaVersion must be a positive integer');
71
+ }
72
+
73
+ const cliVersion = ensureString(source.cliVersion, 'cliVersion');
74
+ const generatedAt = ensureString(source.generatedAt, 'generatedAt');
75
+ const targets = dedupeTargets(ensureArray(source.targets || [], 'targets'));
76
+ const lastUpdateSummary = source.lastUpdateSummary == null
77
+ ? null
78
+ : {
79
+ successfulTargets: Array.from(new Set(ensureArray(source.lastUpdateSummary.successfulTargets || [], 'lastUpdateSummary.successfulTargets'))),
80
+ failedTargets: Array.from(new Set(ensureArray(source.lastUpdateSummary.failedTargets || [], 'lastUpdateSummary.failedTargets'))),
81
+ updatedAt: ensureString(source.lastUpdateSummary.updatedAt, 'lastUpdateSummary.updatedAt')
82
+ };
83
+
84
+ return {
85
+ schemaVersion,
86
+ cliVersion,
87
+ generatedAt,
88
+ targets,
89
+ lastUpdateSummary
90
+ };
91
+ }
92
+
93
+ function createInstallLock({ cliVersion, generatedAt, targets = [], lastUpdateSummary = null }) {
94
+ return normalizeInstallLock({
95
+ schemaVersion: INSTALL_LOCK_VERSION,
96
+ cliVersion,
97
+ generatedAt,
98
+ targets,
99
+ lastUpdateSummary
100
+ });
101
+ }
102
+
103
+ async function readInstallLock(cwd) {
104
+ const lockPath = getInstallLockPath(cwd);
105
+ let raw;
106
+ try {
107
+ raw = await fs.readFile(lockPath, 'utf8');
108
+ } catch (error) {
109
+ if (error && error.code === 'ENOENT') {
110
+ return { exists: false, lockPath, lock: null, error: null };
111
+ }
112
+ throw error;
113
+ }
114
+
115
+ try {
116
+ const parsed = JSON.parse(raw);
117
+ return {
118
+ exists: true,
119
+ lockPath,
120
+ lock: normalizeInstallLock(parsed),
121
+ error: null
122
+ };
123
+ } catch (error) {
124
+ return {
125
+ exists: true,
126
+ lockPath,
127
+ lock: null,
128
+ error: new Error(`Failed to read install-lock: ${error.message}`)
129
+ };
130
+ }
131
+ }
132
+
133
+ async function writeInstallLock(cwd, lockInput) {
134
+ const lockPath = getInstallLockPath(cwd);
135
+ const normalized = normalizeInstallLock(lockInput);
136
+ await fs.mkdir(path.dirname(lockPath), { recursive: true });
137
+ await fs.writeFile(lockPath, `${JSON.stringify(normalized, null, 2)}\n`, 'utf8');
138
+ return { lockPath, lock: normalized };
139
+ }
140
+
141
+ function summarizeTargetState(targetPlan, installedVersion) {
142
+ return normalizeTargetInstallation({
143
+ targetId: targetPlan.targetId,
144
+ targetLabel: targetPlan.targetLabel,
145
+ installedVersion,
146
+ managedFiles: targetPlan.managedFiles,
147
+ ownership: targetPlan.ownership
148
+ });
149
+ }
150
+
151
+ function detectLockDrift(lock, scannedTargets) {
152
+ const lockTargetIds = new Set((lock?.targets || []).map((item) => item.targetId));
153
+ const scannedTargetIds = new Set(scannedTargets.map((item) => item.id));
154
+
155
+ const missingOnDisk = Array.from(lockTargetIds).filter((targetId) => !scannedTargetIds.has(targetId));
156
+ const untrackedOnDisk = Array.from(scannedTargetIds).filter((targetId) => !lockTargetIds.has(targetId));
157
+
158
+ return {
159
+ hasDrift: missingOnDisk.length > 0 || untrackedOnDisk.length > 0,
160
+ missingOnDisk,
161
+ untrackedOnDisk
162
+ };
163
+ }
164
+
165
+ async function detectInstallState(cwd) {
166
+ const lockResult = await readInstallLock(cwd);
167
+ const scannedTargets = await detectInstalledTargets(cwd);
168
+ const lockTargets = lockResult.lock?.targets || [];
169
+ const selectedTargets = lockTargets.length > 0
170
+ ? lockTargets.map((item) => item.targetId)
171
+ : scannedTargets.map((item) => item.id);
172
+
173
+ return {
174
+ lockResult,
175
+ scannedTargets,
176
+ selectedTargets,
177
+ drift: detectLockDrift(lockResult.lock, scannedTargets),
178
+ needsFallback: !lockResult.exists || !!lockResult.error
179
+ };
180
+ }
181
+
182
+ module.exports = {
183
+ INSTALL_LOCK_RELATIVE_PATH,
184
+ INSTALL_LOCK_VERSION,
185
+ createInstallLock,
186
+ detectInstallState,
187
+ detectLockDrift,
188
+ dedupeTargets,
189
+ getInstallLockPath,
190
+ normalizeInstallLock,
191
+ normalizeTargetInstallation,
192
+ readInstallLock,
193
+ summarizeTargetState,
194
+ writeInstallLock
195
+ };
package/lib/manifest.js CHANGED
@@ -1,62 +1,204 @@
1
1
  'use strict';
2
2
 
3
+ const { getTarget } = require('./adapters');
4
+
3
5
  /**
4
6
  * MANAGED_FILES — anws 托管文件清单
5
7
  *
6
8
  * 此数组列出 anws 包负责管理的所有文件路径(相对于目标项目根目录)。
7
9
  */
8
- const MANAGED_FILES = [
9
- 'AGENTS.md',
10
- '.agent/skills/build-inspector/SKILL.md',
11
- '.agent/skills/complexity-guard/references/anti_patterns.md',
12
- '.agent/skills/complexity-guard/SKILL.md',
13
- '.agent/skills/concept-modeler/prompts/GLOSSARY_PROMPT.md',
14
- '.agent/skills/concept-modeler/references/ENTITY_EXTRACTION_PROMPT.md',
15
- '.agent/skills/concept-modeler/scripts/glossary_gen.py',
16
- '.agent/skills/concept-modeler/SKILL.md',
17
- '.agent/skills/design-reviewer/SKILL.md',
18
- '.agent/skills/git-forensics/references/ANALYSIS_METHODOLOGY.md',
19
- '.agent/skills/git-forensics/scripts/git_forensics.py',
20
- '.agent/skills/git-forensics/scripts/git_hotspots.py',
21
- '.agent/skills/git-forensics/SKILL.md',
22
- '.agent/skills/report-template/references/REPORT_TEMPLATE.md',
23
- '.agent/skills/report-template/SKILL.md',
24
- '.agent/skills/runtime-inspector/SKILL.md',
25
- '.agent/skills/spec-writer/references/prd_template.md',
26
- '.agent/skills/spec-writer/SKILL.md',
27
- '.agent/skills/system-architect/references/rfc_template.md',
28
- '.agent/skills/system-architect/SKILL.md',
29
- '.agent/skills/system-designer/references/system-design-detail-template.md',
30
- '.agent/skills/system-designer/references/system-design-template.md',
31
- '.agent/skills/system-designer/SKILL.md',
32
- '.agent/skills/task-planner/references/TASK_TEMPLATE.md',
33
- '.agent/skills/task-planner/SKILL.md',
34
- '.agent/skills/task-reviewer/SKILL.md',
35
- '.agent/skills/tech-evaluator/references/ADR_TEMPLATE.md',
36
- '.agent/skills/tech-evaluator/SKILL.md',
37
- '.agent/workflows/blueprint.md',
38
- '.agent/workflows/challenge.md',
39
- '.agent/workflows/change.md',
40
- '.agent/workflows/craft.md',
41
- '.agent/workflows/design-system.md',
42
- '.agent/workflows/explore.md',
43
- '.agent/workflows/forge.md',
44
- '.agent/workflows/genesis.md',
45
- '.agent/workflows/quickstart.md',
46
- '.agent/workflows/scout.md'
10
+ const RESOURCE_REGISTRY = [
11
+ { id: 'blueprint', type: 'workflow', source: '.agents/workflows/blueprint.md', fileName: 'blueprint.md' },
12
+ { id: 'challenge', type: 'workflow', source: '.agents/workflows/challenge.md', fileName: 'challenge.md' },
13
+ { id: 'change', type: 'workflow', source: '.agents/workflows/change.md', fileName: 'change.md' },
14
+ { id: 'craft', type: 'workflow', source: '.agents/workflows/craft.md', fileName: 'craft.md' },
15
+ { id: 'design-system', type: 'workflow', source: '.agents/workflows/design-system.md', fileName: 'design-system.md' },
16
+ { id: 'explore', type: 'workflow', source: '.agents/workflows/explore.md', fileName: 'explore.md' },
17
+ { id: 'forge', type: 'workflow', source: '.agents/workflows/forge.md', fileName: 'forge.md' },
18
+ { id: 'genesis', type: 'workflow', source: '.agents/workflows/genesis.md', fileName: 'genesis.md' },
19
+ { id: 'probe', type: 'workflow', source: '.agents/workflows/probe.md', fileName: 'probe.md' },
20
+ { id: 'quickstart', type: 'workflow', source: '.agents/workflows/quickstart.md', fileName: 'quickstart.md' },
21
+ { id: 'upgrade', type: 'workflow', source: '.agents/workflows/upgrade.md', fileName: 'upgrade.md' },
22
+ { id: 'concept-modeler', type: 'skill', source: '.agents/skills/concept-modeler/SKILL.md', fileName: 'concept-modeler/SKILL.md' },
23
+ { id: 'design-reviewer', type: 'skill', source: '.agents/skills/design-reviewer/SKILL.md', fileName: 'design-reviewer/SKILL.md' },
24
+ { id: 'nexus-mapper', type: 'skill', source: '.agents/skills/nexus-mapper/SKILL.md', fileName: 'nexus-mapper/SKILL.md' },
25
+ { id: 'nexus-mapper-language-customization', type: 'skill', source: '.agents/skills/nexus-mapper/references/language-customization.md', fileName: 'nexus-mapper/references/language-customization.md' },
26
+ { id: 'nexus-mapper-output-schema', type: 'skill', source: '.agents/skills/nexus-mapper/references/output-schema.md', fileName: 'nexus-mapper/references/output-schema.md' },
27
+ { id: 'nexus-mapper-probe-protocol', type: 'skill', source: '.agents/skills/nexus-mapper/references/probe-protocol.md', fileName: 'nexus-mapper/references/probe-protocol.md' },
28
+ { id: 'nexus-mapper-extract-ast', type: 'skill', source: '.agents/skills/nexus-mapper/scripts/extract_ast.py', fileName: 'nexus-mapper/scripts/extract_ast.py' },
29
+ { id: 'nexus-mapper-git-detective', type: 'skill', source: '.agents/skills/nexus-mapper/scripts/git_detective.py', fileName: 'nexus-mapper/scripts/git_detective.py' },
30
+ { id: 'nexus-mapper-languages', type: 'skill', source: '.agents/skills/nexus-mapper/scripts/languages.json', fileName: 'nexus-mapper/scripts/languages.json' },
31
+ { id: 'nexus-mapper-query-graph', type: 'skill', source: '.agents/skills/nexus-mapper/scripts/query_graph.py', fileName: 'nexus-mapper/scripts/query_graph.py' },
32
+ { id: 'nexus-mapper-requirements', type: 'skill', source: '.agents/skills/nexus-mapper/scripts/requirements.txt', fileName: 'nexus-mapper/scripts/requirements.txt' },
33
+ { id: 'report-template', type: 'skill', source: '.agents/skills/report-template/SKILL.md', fileName: 'report-template/SKILL.md' },
34
+ { id: 'report-template-reference', type: 'skill', source: '.agents/skills/report-template/references/REPORT_TEMPLATE.md', fileName: 'report-template/references/REPORT_TEMPLATE.md' },
35
+ { id: 'runtime-inspector', type: 'skill', source: '.agents/skills/runtime-inspector/SKILL.md', fileName: 'runtime-inspector/SKILL.md' },
36
+ { id: 'sequential-thinking', type: 'skill', source: '.agents/skills/sequential-thinking/SKILL.md', fileName: 'sequential-thinking/SKILL.md' },
37
+ { id: 'spec-writer', type: 'skill', source: '.agents/skills/spec-writer/SKILL.md', fileName: 'spec-writer/SKILL.md' },
38
+ { id: 'spec-writer-prd-template', type: 'skill', source: '.agents/skills/spec-writer/references/prd_template.md', fileName: 'spec-writer/references/prd_template.md' },
39
+ { id: 'system-architect', type: 'skill', source: '.agents/skills/system-architect/SKILL.md', fileName: 'system-architect/SKILL.md' },
40
+ { id: 'system-architect-rfc-template', type: 'skill', source: '.agents/skills/system-architect/references/rfc_template.md', fileName: 'system-architect/references/rfc_template.md' },
41
+ { id: 'system-designer', type: 'skill', source: '.agents/skills/system-designer/SKILL.md', fileName: 'system-designer/SKILL.md' },
42
+ { id: 'system-designer-detail-template', type: 'skill', source: '.agents/skills/system-designer/references/system-design-detail-template.md', fileName: 'system-designer/references/system-design-detail-template.md' },
43
+ { id: 'system-designer-template', type: 'skill', source: '.agents/skills/system-designer/references/system-design-template.md', fileName: 'system-designer/references/system-design-template.md' },
44
+ { id: 'task-planner', type: 'skill', source: '.agents/skills/task-planner/SKILL.md', fileName: 'task-planner/SKILL.md' },
45
+ { id: 'task-planner-template', type: 'skill', source: '.agents/skills/task-planner/references/TASK_TEMPLATE.md', fileName: 'task-planner/references/TASK_TEMPLATE.md' },
46
+ { id: 'task-reviewer', type: 'skill', source: '.agents/skills/task-reviewer/SKILL.md', fileName: 'task-reviewer/SKILL.md' },
47
+ { id: 'tech-evaluator', type: 'skill', source: '.agents/skills/tech-evaluator/SKILL.md', fileName: 'tech-evaluator/SKILL.md' },
48
+ { id: 'tech-evaluator-adr-template', type: 'skill', source: '.agents/skills/tech-evaluator/references/ADR_TEMPLATE.md', fileName: 'tech-evaluator/references/ADR_TEMPLATE.md' }
47
49
  ];
48
50
 
51
+ function toArray(value) {
52
+ return Array.isArray(value) ? value : [value];
53
+ }
54
+
55
+ function toProjectionFileName(resource, projectionType, targetId) {
56
+ if (targetId === 'codex' && projectionType === 'skills' && resource.type === 'workflow') {
57
+ return resource.id === 'quickstart'
58
+ ? 'anws-system/SKILL.md'
59
+ : `anws-system/references/${resource.id}.md`;
60
+ }
61
+ if (projectionType === 'commands') {
62
+ return `${resource.id}.md`;
63
+ }
64
+ if (projectionType === 'prompts') {
65
+ return targetId === 'copilot' ? `${resource.id}.prompt.md` : `${resource.id}.md`;
66
+ }
67
+ if (projectionType === 'agents') {
68
+ return `${resource.id}.md`;
69
+ }
70
+ return resource.fileName;
71
+ }
72
+
73
+ function buildProjectionEntries(targetId) {
74
+ const target = getTarget(targetId);
75
+ const typeMap = target.projectionTypes;
76
+
77
+ return RESOURCE_REGISTRY.flatMap((resource) => {
78
+ const projectionTypes = typeMap[resource.type];
79
+ if (!projectionTypes) {
80
+ return [];
81
+ }
82
+
83
+ return toArray(projectionTypes).map((projectionType) => {
84
+ const outputFileName = toProjectionFileName(resource, projectionType, target.id);
85
+ return {
86
+ ...resource,
87
+ projectionType,
88
+ outputRoot: target.projections[projectionType],
89
+ outputPath: `${target.projections[projectionType]}/${outputFileName}`
90
+ };
91
+ });
92
+ });
93
+ }
94
+
95
+ function buildManagedManifest(targetIds = ['antigravity']) {
96
+ return toArray(targetIds).flatMap((targetId) => {
97
+ const target = getTarget(targetId);
98
+ const entries = buildProjectionEntries(target.id).map((entry) => ({
99
+ ...entry,
100
+ targetId: target.id,
101
+ targetLabel: target.label,
102
+ ownershipKey: `${target.id}:${entry.outputPath}`
103
+ }));
104
+
105
+ if (!target.rootAgentFile) {
106
+ return entries;
107
+ }
108
+
109
+ return [
110
+ {
111
+ id: 'root-agents',
112
+ type: 'root',
113
+ source: 'AGENTS.md',
114
+ fileName: 'AGENTS.md',
115
+ projectionType: 'rootAgentFile',
116
+ outputRoot: '.',
117
+ outputPath: 'AGENTS.md',
118
+ targetId: target.id,
119
+ targetLabel: target.label,
120
+ ownershipKey: `${target.id}:AGENTS.md`
121
+ },
122
+ ...entries
123
+ ];
124
+ });
125
+ }
126
+
127
+ function buildProjectionPlan(targetIds = ['antigravity'], resources = RESOURCE_REGISTRY) {
128
+ return toArray(targetIds).map((targetId) => {
129
+ const target = getTarget(targetId);
130
+ const typeMap = target.projectionTypes;
131
+ const projectionEntries = resources.flatMap((resource) => {
132
+ const projectionTypes = typeMap[resource.type];
133
+ if (!projectionTypes) {
134
+ return [];
135
+ }
136
+
137
+ return toArray(projectionTypes).map((projectionType) => {
138
+ const outputFileName = toProjectionFileName(resource, projectionType, target.id);
139
+ const outputPath = `${target.projections[projectionType]}/${outputFileName}`;
140
+ return {
141
+ ...resource,
142
+ projectionType,
143
+ outputRoot: target.projections[projectionType],
144
+ outputPath,
145
+ targetId: target.id,
146
+ targetLabel: target.label,
147
+ ownershipKey: `${target.id}:${outputPath}`
148
+ };
149
+ });
150
+ });
151
+
152
+ const managedFiles = target.rootAgentFile
153
+ ? ['AGENTS.md', ...projectionEntries.map((item) => item.outputPath)]
154
+ : projectionEntries.map((item) => item.outputPath);
155
+
156
+ return {
157
+ target,
158
+ targetId: target.id,
159
+ targetLabel: target.label,
160
+ managedFiles,
161
+ userProtectedFiles: buildUserProtectedFiles(target.id),
162
+ projectionEntries,
163
+ ownership: projectionEntries.map((item) => item.ownershipKey)
164
+ };
165
+ });
166
+ }
167
+
168
+ function buildManagedFiles(targetId = 'antigravity') {
169
+ return buildManagedManifest(targetId).map((item) => item.outputPath);
170
+ }
171
+
172
+ function buildUserProtectedFiles(targetId = 'antigravity') {
173
+ const target = getTarget(targetId);
174
+ return target.rootAgentFile ? ['AGENTS.md'] : [];
175
+ }
176
+
177
+ function findByType(type) {
178
+ return RESOURCE_REGISTRY.filter((item) => item.type === type);
179
+ }
180
+
181
+ const MANAGED_FILES = buildManagedFiles('antigravity');
182
+
49
183
  /**
50
184
  * USER_PROTECTED_FILES — 用户保护文件
51
185
  *
52
186
  * 这些文件在项目初始化后通常会包含特定于项目的配置。
53
187
  * anws update 默认会跳过这些文件。
54
188
  */
55
- const USER_PROTECTED_FILES = [
56
- 'AGENTS.md'
57
- ];
189
+ const USER_PROTECTED_FILES = buildUserProtectedFiles('antigravity');
58
190
 
59
191
  module.exports = {
192
+ RESOURCE_REGISTRY,
193
+ buildManagedManifest,
194
+ buildProjectionPlan,
195
+ buildManagedFiles,
196
+ buildProjectionEntries,
197
+ buildUserProtectedFiles,
198
+ findByType,
60
199
  MANAGED_FILES,
61
200
  USER_PROTECTED_FILES
62
201
  };
202
+
203
+
204
+
package/lib/output.js CHANGED
@@ -16,8 +16,20 @@ const useColor =
16
16
  !process.env.NO_COLOR &&
17
17
  process.env.TERM !== 'dumb';
18
18
 
19
+ const PALETTE = {
20
+ ink: [242, 244, 246],
21
+ muted: [159, 166, 174],
22
+ brand: [127, 181, 182],
23
+ frost: [242, 244, 246],
24
+ deep: [31, 34, 38],
25
+ };
26
+
19
27
  // ─── ANSI 转义码 ──────────────────────────────────────────────────────────────
20
28
  const c = {
29
+ brand: useColor ? '\x1b[38;2;127;181;182m' : '',
30
+ ink: useColor ? '\x1b[38;2;242;244;246m' : '',
31
+ muted: useColor ? '\x1b[38;2;159;166;174m' : '',
32
+ deep: useColor ? '\x1b[38;2;31;34;38m' : '',
21
33
  green: useColor ? '\x1b[32m' : '',
22
34
  yellow: useColor ? '\x1b[33m' : '',
23
35
  red: useColor ? '\x1b[31m' : '',
@@ -25,6 +37,151 @@ const c = {
25
37
  reset: useColor ? '\x1b[0m' : '',
26
38
  };
27
39
 
40
+ function mixRgb(start, end, ratio) {
41
+ return [
42
+ Math.round(start[0] + (end[0] - start[0]) * ratio),
43
+ Math.round(start[1] + (end[1] - start[1]) * ratio),
44
+ Math.round(start[2] + (end[2] - start[2]) * ratio),
45
+ ];
46
+ }
47
+
48
+ function colorRgb(rgb) {
49
+ return useColor ? `\x1b[38;2;${rgb[0]};${rgb[1]};${rgb[2]}m` : '';
50
+ }
51
+
52
+ function colorize(text, tone) {
53
+ if (!useColor || !tone) return text;
54
+ const color = Array.isArray(tone) ? colorRgb(tone) : tone;
55
+ return `${color}${text}${c.reset}`;
56
+ }
57
+
58
+ function visibleLength(text) {
59
+ return String(text).replace(/\x1b\[[0-9;]*m/g, '').length;
60
+ }
61
+
62
+ function mixStops(stops, ratio) {
63
+ if (stops.length === 0) return [255, 255, 255];
64
+ if (stops.length === 1) return stops[0];
65
+
66
+ const clamped = Math.max(0, Math.min(1, ratio));
67
+ const scaled = clamped * (stops.length - 1);
68
+ const leftIndex = Math.floor(scaled);
69
+ const rightIndex = Math.min(stops.length - 1, leftIndex + 1);
70
+ const localRatio = scaled - leftIndex;
71
+ return mixRgb(stops[leftIndex], stops[rightIndex], localRatio);
72
+ }
73
+
74
+ function stylizeAsciiLine(text, rowIndex, rowCount) {
75
+ if (!useColor) return text;
76
+
77
+ const rowRatio = rowCount <= 1 ? 0 : rowIndex / (rowCount - 1);
78
+ const softenedTop = Math.pow(rowRatio, 1.12);
79
+ const rowBase = mixStops(
80
+ [PALETTE.deep, PALETTE.brand, PALETTE.muted, PALETTE.ink],
81
+ 0.08 + softenedTop * 0.8
82
+ );
83
+ const center = text.length / 2;
84
+
85
+ let output = '';
86
+ for (let index = 0; index < text.length; index += 1) {
87
+ const char = text[index];
88
+ if (char === ' ') {
89
+ output += char;
90
+ continue;
91
+ }
92
+
93
+ const distance = Math.abs(index - center) / Math.max(1, center);
94
+ const highlight = Math.max(0, 1 - distance);
95
+ const horizontalRatio = text.length <= 1 ? 0 : index / (text.length - 1);
96
+ const sweep = mixStops(
97
+ [PALETTE.deep, PALETTE.brand, PALETTE.frost, PALETTE.muted, PALETTE.ink],
98
+ horizontalRatio
99
+ );
100
+ const brandLift = mixRgb(rowBase, PALETTE.brand, 0.14 + highlight * 0.16);
101
+ const cooled = mixRgb(brandLift, sweep, 0.4 + highlight * 0.18);
102
+ const lowerGlow = Math.max(0, rowRatio - 0.58) / 0.42;
103
+ const tone = mixRgb(cooled, PALETTE.ink, 0.02 + highlight * 0.16 + lowerGlow * 0.16);
104
+ output += `${colorRgb(tone)}${char}`;
105
+ }
106
+
107
+ return output + c.reset;
108
+ }
109
+
110
+ function centerLine(text, width) {
111
+ const length = visibleLength(text);
112
+ if (!width || length >= width) return text;
113
+ const leftPad = Math.max(0, Math.floor((width - length) / 2));
114
+ return `${' '.repeat(leftPad)}${text}`;
115
+ }
116
+
117
+ function stylizeTagline(text) {
118
+ if (!useColor) return text;
119
+
120
+ let output = '';
121
+ const length = text.length;
122
+ for (let index = 0; index < length; index += 1) {
123
+ const char = text[index];
124
+ if (char === ' ') {
125
+ output += char;
126
+ continue;
127
+ }
128
+
129
+ const ratio = length <= 1 ? 0 : index / (length - 1);
130
+ const wave = Math.abs(ratio - 0.5) * 2;
131
+ const base = mixStops(
132
+ [PALETTE.deep, PALETTE.brand, PALETTE.muted, PALETTE.ink],
133
+ ratio
134
+ );
135
+ const tone = mixRgb(base, PALETTE.ink, 0.08 + (1 - wave) * 0.18);
136
+ output += `${colorRgb(tone)}${char}`;
137
+ }
138
+
139
+ return output + c.reset;
140
+ }
141
+
142
+ function padText(text, width) {
143
+ const padding = Math.max(0, width - visibleLength(text));
144
+ return `${text}${' '.repeat(padding)}`;
145
+ }
146
+
147
+ function drawBox(options = {}) {
148
+ const title = options.title || '';
149
+ const lines = Array.isArray(options.lines) ? options.lines : [];
150
+ const accent = options.accent || PALETTE.brand;
151
+ const borderTone = options.borderTone || PALETTE.muted;
152
+ const textTone = options.textTone || c.ink;
153
+ const minWidth = Number.isInteger(options.minWidth) ? options.minWidth : 20;
154
+ const innerWidth = Math.max(minWidth, visibleLength(title), ...lines.map((line) => visibleLength(line)));
155
+ const titleText = title ? ` ${title} ` : '';
156
+ const top = `╭${titleText}${'─'.repeat(Math.max(0, innerWidth + 2 - visibleLength(titleText)))}╮`;
157
+ const bottom = `╰${'─'.repeat(innerWidth + 2)}╯`;
158
+ const body = lines.map((line) => {
159
+ // 支持分隔线:以 '---' 开头的行渲染为水平分隔线
160
+ if (line.startsWith('---')) {
161
+ const sepInner = '─'.repeat(innerWidth);
162
+ return `${colorize('│', borderTone)}${colorize(sepInner, borderTone)}${colorize('│', borderTone)}`;
163
+ }
164
+ const padded = padText(line, innerWidth);
165
+ return `${colorize('│', borderTone)} ${colorize(padded, textTone)} ${colorize('│', borderTone)}`;
166
+ });
167
+
168
+ return [
169
+ colorize(top, accent),
170
+ ...body,
171
+ colorize(bottom, borderTone)
172
+ ].join('\n');
173
+ }
174
+
175
+ function section(title, lines = [], options = {}) {
176
+ console.log(drawBox({
177
+ title,
178
+ lines,
179
+ accent: options.accent || PALETTE.brand,
180
+ borderTone: options.borderTone || PALETTE.muted,
181
+ textTone: options.textTone || c.ink,
182
+ minWidth: options.minWidth
183
+ }));
184
+ }
28
185
  // ─── 公共输出函数 ─────────────────────────────────────────────────────────────
29
186
 
30
187
  /** 成功消息(绿色 ✔)*/
@@ -64,16 +221,31 @@ function blank() {
64
221
 
65
222
  /** 打印 ASCII Logo */
66
223
  function logo() {
67
- const art = `
68
- ___ _ ___ _______
69
- / | / | / / | /| / / ___/
70
- / /| | / |/ /| |/ |/ /\\__ \\
71
- / ___ |/ /| / | /| /___/ /
72
- /_/ |_/_/ |_/ |__/|__//____/
73
- `;
74
- const cyan = useColor ? '\x1b[36m' : '';
75
- const reset = useColor ? '\x1b[0m' : '';
76
- console.log(`${cyan}${art}${reset}`);
77
- }
78
-
79
- module.exports = { success, warn, error, info, fileLine, skippedLine, blank, logo, c };
224
+ const terminalWidth = Number.isInteger(process.stdout.columns) ? process.stdout.columns : 0;
225
+ const art = [
226
+ '█████╗ ███╗ ██╗██╗ ██╗███████╗',
227
+ '██╔══██╗████╗ ██║██║ ██║██╔════╝',
228
+ '███████║██╔██╗ ██║██║ █╗ ██║███████╗',
229
+ '██╔══██║██║╚██╗██║██║███╗██║╚════██║',
230
+ '██║ ██║██║ ╚████║╚███╔███╔╝███████║',
231
+ '╚═╝ ╚═╝╚═╝ ╚═══╝ ╚══╝╚══╝ ╚══════╝',
232
+ ];
233
+ const title = art
234
+ .map((line, index, lines) => {
235
+ const centered = centerLine(line, terminalWidth);
236
+ return useColor ? stylizeAsciiLine(centered, index, lines.length) : centered;
237
+ })
238
+ .join('\n');
239
+ const taglineText = centerLine('‹ Axiom · Nexus · Weave · Sovereignty ›', terminalWidth);
240
+ const tagline = useColor ? stylizeTagline(taglineText) : taglineText;
241
+
242
+ console.log(title);
243
+ console.log(tagline);
244
+ }
245
+
246
+ module.exports = { success, warn, error, info, fileLine, skippedLine, blank, logo, c, PALETTE, colorRgb, colorize, visibleLength, drawBox, section };
247
+
248
+
249
+
250
+
251
+