@archsight/aios 1.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/AGENTS.md +24 -0
- package/AI_CODING_RULES.md +117 -0
- package/CHANGELOG.md +17 -0
- package/CLAUDE.md +23 -0
- package/CODE_OF_CONDUCT.md +23 -0
- package/CONTRIBUTING.md +60 -0
- package/GEMINI.md +24 -0
- package/LICENSE +21 -0
- package/README.md +154 -0
- package/SECURITY.md +32 -0
- package/agents/README.md +68 -0
- package/agents/argus/constraints.md +37 -0
- package/agents/argus/responsibilities.md +29 -0
- package/agents/argus/role.md +35 -0
- package/agents/argus/system-prompt.md +69 -0
- package/agents/argus/workflow.md +60 -0
- package/agents/athena/constraints.md +7 -0
- package/agents/athena/responsibilities.md +7 -0
- package/agents/athena/role.md +13 -0
- package/agents/athena/system-prompt.md +23 -0
- package/agents/athena/workflow.md +8 -0
- package/agents/atlas/constraints.md +40 -0
- package/agents/atlas/responsibilities.md +37 -0
- package/agents/atlas/role.md +37 -0
- package/agents/atlas/system-prompt.md +80 -0
- package/agents/atlas/workflow.md +82 -0
- package/agents/daedalus/constraints.md +37 -0
- package/agents/daedalus/responsibilities.md +29 -0
- package/agents/daedalus/role.md +36 -0
- package/agents/daedalus/system-prompt.md +67 -0
- package/agents/daedalus/workflow.md +63 -0
- package/agents/euclid/constraints.md +8 -0
- package/agents/euclid/responsibilities.md +8 -0
- package/agents/euclid/role.md +13 -0
- package/agents/euclid/system-prompt.md +23 -0
- package/agents/euclid/workflow.md +8 -0
- package/agents/hephaestus/constraints.md +39 -0
- package/agents/hephaestus/responsibilities.md +29 -0
- package/agents/hephaestus/role.md +35 -0
- package/agents/hephaestus/system-prompt.md +70 -0
- package/agents/hephaestus/workflow.md +62 -0
- package/agents/janus/constraints.md +7 -0
- package/agents/janus/responsibilities.md +7 -0
- package/agents/janus/role.md +13 -0
- package/agents/janus/system-prompt.md +23 -0
- package/agents/janus/workflow.md +8 -0
- package/agents/mason/constraints.md +37 -0
- package/agents/mason/responsibilities.md +33 -0
- package/agents/mason/role.md +35 -0
- package/agents/mason/system-prompt.md +69 -0
- package/agents/mason/workflow.md +83 -0
- package/agents/mercury/constraints.md +7 -0
- package/agents/mercury/responsibilities.md +7 -0
- package/agents/mercury/role.md +13 -0
- package/agents/mercury/system-prompt.md +23 -0
- package/agents/mercury/workflow.md +8 -0
- package/agents/vitruvius/constraints.md +37 -0
- package/agents/vitruvius/responsibilities.md +28 -0
- package/agents/vitruvius/role.md +35 -0
- package/agents/vitruvius/system-prompt.md +66 -0
- package/agents/vitruvius/workflow.md +59 -0
- package/bin/archsight-aios.mjs +959 -0
- package/delivery/README.md +13 -0
- package/delivery/ai-generated-code-checklist.md +15 -0
- package/delivery/release-checklist.md +23 -0
- package/delivery/rollback-policy.md +26 -0
- package/docs/ai-engineering-squad-plan.md +506 -0
- package/docs/ai-team-os-repository-architecture.md +459 -0
- package/docs/business-expert-guide.md +67 -0
- package/docs/glossary.md +65 -0
- package/docs/quickstart.md +88 -0
- package/governance/README.md +15 -0
- package/governance/agent-boundary.md +27 -0
- package/governance/ai-review-policy.md +27 -0
- package/governance/coding-rules.md +9 -0
- package/governance/context-policy.md +24 -0
- package/governance/delivery-policy.md +21 -0
- package/governance/memory-policy.md +25 -0
- package/governance/security-policy.md +25 -0
- package/graph/README.md +12 -0
- package/graph/quality-policy.md +14 -0
- package/graph/schema.md +28 -0
- package/infra/README.md +21 -0
- package/infra/environment-policy.md +21 -0
- package/infra/permissions.md +21 -0
- package/infra/service-boundaries.md +18 -0
- package/knowledge/README.md +20 -0
- package/knowledge/domain-taxonomy.md +19 -0
- package/knowledge/source-register.md +23 -0
- package/memory/README.md +12 -0
- package/memory/cleanup-policy.md +15 -0
- package/memory/decision-records.md +26 -0
- package/memory/project-memory-policy.md +18 -0
- package/package.json +69 -0
- package/prompts/README.md +13 -0
- package/prompts/evaluation-policy.md +18 -0
- package/prompts/failure-cases.md +23 -0
- package/prompts/prompt-registry.md +24 -0
- package/rag/README.md +12 -0
- package/rag/chunking-policy.md +19 -0
- package/rag/evaluation-policy.md +16 -0
- package/runtime/README.md +22 -0
- package/runtime/agent-routing.md +92 -0
- package/runtime/archsight-aios.manifest.json +326 -0
- package/runtime/hermes/agent-registry.md +58 -0
- package/runtime/hermes/feishu-routing.md +60 -0
- package/runtime/hermes/sync-from-agents.md +48 -0
- package/runtime/hermes/sync-policy.md +20 -0
- package/runtime/hermes/sync-record-template.md +18 -0
- package/runtime/hermes/workspace-binding.md +55 -0
- package/runtime/skill-routing.md +63 -0
- package/skills/README.md +38 -0
- package/skills/aios-arch/SKILL.md +175 -0
- package/skills/aios-arch/agents/openai.yaml +4 -0
- package/skills/aios-ceo/SKILL.md +89 -0
- package/skills/aios-ceo/agents/openai.yaml +4 -0
- package/skills/aios-design/SKILL.md +99 -0
- package/skills/aios-design/agents/openai.yaml +4 -0
- package/skills/aios-exec/SKILL.md +62 -0
- package/skills/aios-exec/agents/openai.yaml +4 -0
- package/skills/aios-knowledge/SKILL.md +61 -0
- package/skills/aios-knowledge/agents/openai.yaml +4 -0
- package/skills/aios-plan/SKILL.md +96 -0
- package/skills/aios-plan/agents/openai.yaml +4 -0
- package/skills/aios-review/SKILL.md +66 -0
- package/skills/aios-review/agents/openai.yaml +4 -0
- package/skills/aios-runtime/SKILL.md +65 -0
- package/skills/aios-runtime/agents/openai.yaml +4 -0
- package/standards/README.md +16 -0
- package/standards/standard-register.md +23 -0
- package/templates/README.md +29 -0
- package/templates/project-ai/.ai/ARCHSIGHT_AIOS_RULES.md +35 -0
- package/templates/project-ai/.ai/agent-routing.md +50 -0
- package/templates/project-ai/.ai/project-context.md +47 -0
- package/templates/project-ai/.ai/skills.md +39 -0
- package/templates/project-ai/.ai/workflows.md +40 -0
- package/templates/project-ai/AGENTS.md +25 -0
- package/templates/project-ai/AI_CODING_RULES.md +118 -0
- package/templates/project-ai/CLAUDE.md +25 -0
- package/templates/project-ai/GEMINI.md +25 -0
- package/templates/project-bim-platform/.ai/profiles/bim-platform.md +39 -0
- package/templates/project-construction-vision/.ai/profiles/construction-vision.md +39 -0
- package/templates/project-rag-knowledge/.ai/profiles/rag-knowledge.md +40 -0
- package/templates/template-expansion-backlog.md +29 -0
- package/vision/README.md +15 -0
- package/vision/roadmap.md +29 -0
- package/vision/strategy-principles.md +16 -0
- package/workflows/README.md +19 -0
- package/workflows/architecture-review.md +98 -0
- package/workflows/bug-fixing.md +62 -0
- package/workflows/code-review.md +54 -0
- package/workflows/design-review.md +68 -0
- package/workflows/feature-development.md +72 -0
- package/workflows/frontend-generation.md +60 -0
- package/workflows/quality-readiness.md +74 -0
- package/workflows/rag-pipeline.md +57 -0
- package/workflows/release.md +58 -0
- package/workflows/review.md +70 -0
|
@@ -0,0 +1,959 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
const repoRoot = path.resolve(__dirname, "..");
|
|
11
|
+
const home = os.homedir();
|
|
12
|
+
|
|
13
|
+
const managedStart = "<!-- ARCHSIGHT-AIOS:START -->";
|
|
14
|
+
const managedEnd = "<!-- ARCHSIGHT-AIOS:END -->";
|
|
15
|
+
const antigravityPluginName = "archsight-aios";
|
|
16
|
+
|
|
17
|
+
const assetDirs = [
|
|
18
|
+
"skills",
|
|
19
|
+
"workflows",
|
|
20
|
+
"templates",
|
|
21
|
+
"runtime",
|
|
22
|
+
"agents",
|
|
23
|
+
"governance",
|
|
24
|
+
"delivery",
|
|
25
|
+
"memory",
|
|
26
|
+
"knowledge",
|
|
27
|
+
"rag",
|
|
28
|
+
"graph",
|
|
29
|
+
"standards",
|
|
30
|
+
"infra",
|
|
31
|
+
"prompts",
|
|
32
|
+
"vision",
|
|
33
|
+
"docs"
|
|
34
|
+
];
|
|
35
|
+
const assetFiles = ["README.md", "AI_CODING_RULES.md", "AGENTS.md", "CLAUDE.md", "GEMINI.md"];
|
|
36
|
+
const skillAliases = {
|
|
37
|
+
"aios-arch": ["aios-architecture-review", "archsight-architecture-review"],
|
|
38
|
+
"aios-plan": ["aios-delivery-planning", "archsight-delivery-planning"],
|
|
39
|
+
"aios-review": ["aios-code-review", "archsight-code-review"],
|
|
40
|
+
"aios-knowledge": [
|
|
41
|
+
"aios-building-knowledge",
|
|
42
|
+
"archsight-building-knowledge",
|
|
43
|
+
"aios-bim-domain-modeling",
|
|
44
|
+
"archsight-bim-domain-modeling"
|
|
45
|
+
],
|
|
46
|
+
"aios-runtime": [
|
|
47
|
+
"aios-runtime-design",
|
|
48
|
+
"archsight-runtime-design",
|
|
49
|
+
"aios-ai-runtime-design",
|
|
50
|
+
"archsight-ai-runtime-design"
|
|
51
|
+
],
|
|
52
|
+
"aios-exec": ["aios-controlled-execution", "archsight-controlled-execution"]
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function usage() {
|
|
56
|
+
return [
|
|
57
|
+
"ArchSight AIOS",
|
|
58
|
+
"",
|
|
59
|
+
"Usage:",
|
|
60
|
+
" archsight-aios help",
|
|
61
|
+
" archsight-aios install --target <codex|agents|gemini|antigravity|all> --scope user",
|
|
62
|
+
" archsight-aios doctor",
|
|
63
|
+
" archsight-aios init [--cwd <path>] [--mode <auto|full|linked|ai-only>] [--profile <name>]",
|
|
64
|
+
" archsight-aios validate [--cwd <path>] [--profile <name>] [--temp]",
|
|
65
|
+
" archsight-aios hermes:validate",
|
|
66
|
+
" archsight-aios hermes:sync-dry-run",
|
|
67
|
+
" archsight-aios hermes:detect-drift",
|
|
68
|
+
"",
|
|
69
|
+
"Commands:",
|
|
70
|
+
" help Show this help.",
|
|
71
|
+
" install Install AIOS assets into user-level assistant locations.",
|
|
72
|
+
" doctor Check repository assets and user-level installation.",
|
|
73
|
+
" init Add AI rules and .ai governance files to a project.",
|
|
74
|
+
" validate Validate the project AI template output.",
|
|
75
|
+
" hermes:* Validate or dry-run Hermes runtime prompt sync.",
|
|
76
|
+
"",
|
|
77
|
+
"Examples:",
|
|
78
|
+
" npx @archsight/aios install --target codex --scope user",
|
|
79
|
+
" npx @archsight/aios install --target agents --scope user",
|
|
80
|
+
" npx @archsight/aios init",
|
|
81
|
+
" npx @archsight/aios validate --temp",
|
|
82
|
+
" npx @archsight/aios doctor"
|
|
83
|
+
].join("\n");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function parseArgs(argv) {
|
|
87
|
+
const [command, ...rest] = argv;
|
|
88
|
+
const help = command === "--help" || command === "-h" || command === "help";
|
|
89
|
+
const options = {
|
|
90
|
+
command: help ? undefined : command,
|
|
91
|
+
target: "all",
|
|
92
|
+
scope: "user",
|
|
93
|
+
mode: "auto",
|
|
94
|
+
profile: undefined,
|
|
95
|
+
cwd: process.cwd(),
|
|
96
|
+
help,
|
|
97
|
+
temp: false
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
101
|
+
const arg = rest[i];
|
|
102
|
+
if (arg === "--target") {
|
|
103
|
+
options.target = rest[++i];
|
|
104
|
+
} else if (arg === "--scope") {
|
|
105
|
+
options.scope = rest[++i];
|
|
106
|
+
} else if (arg === "--cwd") {
|
|
107
|
+
options.cwd = path.resolve(rest[++i]);
|
|
108
|
+
} else if (arg === "--mode") {
|
|
109
|
+
options.mode = rest[++i];
|
|
110
|
+
} else if (arg === "--profile") {
|
|
111
|
+
options.profile = rest[++i];
|
|
112
|
+
} else if (arg === "--temp") {
|
|
113
|
+
options.temp = true;
|
|
114
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
115
|
+
options.help = true;
|
|
116
|
+
} else {
|
|
117
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return options;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function exists(targetPath) {
|
|
125
|
+
try {
|
|
126
|
+
await fs.access(targetPath);
|
|
127
|
+
return true;
|
|
128
|
+
} catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function ensureDir(targetPath) {
|
|
134
|
+
await fs.mkdir(targetPath, { recursive: true });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function assertInside(child, parent) {
|
|
138
|
+
const relative = path.relative(path.resolve(parent), path.resolve(child));
|
|
139
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
140
|
+
throw new Error(`Refusing to write outside ${parent}: ${child}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function copyDir(src, dest) {
|
|
145
|
+
if (!(await exists(src))) {
|
|
146
|
+
throw new Error(`Missing source directory: ${src}`);
|
|
147
|
+
}
|
|
148
|
+
assertInside(dest, path.dirname(dest));
|
|
149
|
+
await fs.rm(dest, { recursive: true, force: true });
|
|
150
|
+
await fs.cp(src, dest, { recursive: true });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function copyFileIfExists(src, dest) {
|
|
154
|
+
if (await exists(src)) {
|
|
155
|
+
await ensureDir(path.dirname(dest));
|
|
156
|
+
await fs.copyFile(src, dest);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function copyTreeMissing(srcRoot, destRoot) {
|
|
161
|
+
if (!(await exists(srcRoot))) {
|
|
162
|
+
throw new Error(`Missing source directory: ${srcRoot}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const entries = await fs.readdir(srcRoot, { withFileTypes: true });
|
|
166
|
+
for (const entry of entries) {
|
|
167
|
+
const src = path.join(srcRoot, entry.name);
|
|
168
|
+
const dest = path.join(destRoot, entry.name);
|
|
169
|
+
assertInside(dest, destRoot);
|
|
170
|
+
if (entry.isDirectory()) {
|
|
171
|
+
await copyTreeMissing(src, dest);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (await exists(dest)) {
|
|
175
|
+
console.log(`SKIP existing ${dest}`);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
await ensureDir(path.dirname(dest));
|
|
179
|
+
await fs.copyFile(src, dest);
|
|
180
|
+
console.log(`CREATE ${dest}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function listAiosSkills() {
|
|
185
|
+
const manifest = await readManifest();
|
|
186
|
+
if (manifest?.skills?.length > 0) {
|
|
187
|
+
return manifest.skills.map((skill) => skill.id).sort();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const skillsRoot = path.join(repoRoot, "skills");
|
|
191
|
+
const entries = await fs.readdir(skillsRoot, { withFileTypes: true });
|
|
192
|
+
return entries
|
|
193
|
+
.filter((entry) => entry.isDirectory() && entry.name.startsWith("aios-"))
|
|
194
|
+
.map((entry) => entry.name)
|
|
195
|
+
.sort();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function listAiosWorkflowPaths() {
|
|
199
|
+
const manifest = await readManifest();
|
|
200
|
+
return (manifest.workflows ?? [])
|
|
201
|
+
.map((workflow) => workflow.path)
|
|
202
|
+
.sort();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function readManifest() {
|
|
206
|
+
const manifestPath = path.join(repoRoot, "runtime", "archsight-aios.manifest.json");
|
|
207
|
+
const raw = await fs.readFile(manifestPath, "utf8");
|
|
208
|
+
return JSON.parse(raw);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function readJson(filePath) {
|
|
212
|
+
const raw = await fs.readFile(filePath, "utf8");
|
|
213
|
+
return JSON.parse(raw);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function routeAgentName(manifest, agentId) {
|
|
217
|
+
return manifest.agents.find((agent) => agent.id === agentId)?.displayName ?? agentId;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function fileContains(filePath, needle) {
|
|
221
|
+
if (!(await exists(filePath))) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
225
|
+
return content.includes(needle);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function readTextIfExists(filePath) {
|
|
229
|
+
if (!(await exists(filePath))) {
|
|
230
|
+
return "";
|
|
231
|
+
}
|
|
232
|
+
return fs.readFile(filePath, "utf8");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function expectedSkillDir(skill) {
|
|
236
|
+
return path.dirname(skill.path);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function geminiContentRoot() {
|
|
240
|
+
return path.join(home, ".gemini", "archsight-aios");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function syncGeminiContent() {
|
|
244
|
+
const contentRoot = geminiContentRoot();
|
|
245
|
+
await ensureDir(contentRoot);
|
|
246
|
+
|
|
247
|
+
for (const dirName of assetDirs) {
|
|
248
|
+
await copyDir(path.join(repoRoot, dirName), path.join(contentRoot, dirName));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
for (const fileName of assetFiles) {
|
|
252
|
+
await copyFileIfExists(path.join(repoRoot, fileName), path.join(contentRoot, fileName));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return contentRoot;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function installSkillsTo(targetRoot, skillNames) {
|
|
259
|
+
await ensureDir(targetRoot);
|
|
260
|
+
for (const skillName of skillNames) {
|
|
261
|
+
await copyDir(
|
|
262
|
+
path.join(repoRoot, "skills", skillName),
|
|
263
|
+
path.join(targetRoot, skillName)
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async function writeJson(filePath, value) {
|
|
269
|
+
await ensureDir(path.dirname(filePath));
|
|
270
|
+
await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function antigravityLegacyRoot() {
|
|
274
|
+
return path.join(home, ".gemini", "antigravity");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function antigravityLegacySkillsRoot() {
|
|
278
|
+
return path.join(antigravityLegacyRoot(), "skills");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function antigravityPluginsRoot() {
|
|
282
|
+
return path.join(home, ".gemini", "config", "plugins");
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function antigravityPluginRoot() {
|
|
286
|
+
return path.join(antigravityPluginsRoot(), antigravityPluginName);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async function hasAntigravity2Config() {
|
|
290
|
+
if (!(await exists(path.join(home, ".gemini", "config")))) {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const installMarkers = [
|
|
295
|
+
path.join(home, ".gemini", "antigravity-ide"),
|
|
296
|
+
path.join(home, ".gemini", "antigravity-cli"),
|
|
297
|
+
path.join(home, ".antigravity-ide"),
|
|
298
|
+
path.join(home, ".antigravity-cli"),
|
|
299
|
+
path.join(home, ".antigravitycli")
|
|
300
|
+
];
|
|
301
|
+
|
|
302
|
+
for (const marker of installMarkers) {
|
|
303
|
+
if (await exists(marker)) {
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async function installAntigravityPlugin(skillNames) {
|
|
312
|
+
const pluginRoot = antigravityPluginRoot();
|
|
313
|
+
assertInside(pluginRoot, antigravityPluginsRoot());
|
|
314
|
+
await fs.rm(pluginRoot, { recursive: true, force: true });
|
|
315
|
+
await ensureDir(pluginRoot);
|
|
316
|
+
await writeJson(path.join(pluginRoot, "plugin.json"), { name: antigravityPluginName });
|
|
317
|
+
await installSkillsTo(path.join(pluginRoot, "skills"), skillNames);
|
|
318
|
+
return pluginRoot;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async function installAntigravityLegacy(skillNames) {
|
|
322
|
+
const skillsRoot = antigravityLegacySkillsRoot();
|
|
323
|
+
await removeLegacySkillDirs(skillsRoot, skillNames);
|
|
324
|
+
await installSkillsTo(skillsRoot, skillNames);
|
|
325
|
+
return skillsRoot;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async function removeLegacySkillDirs(targetRoot, skillNames) {
|
|
329
|
+
for (const skillName of skillNames) {
|
|
330
|
+
if (!skillName.startsWith("aios-")) {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const legacyNames = [
|
|
335
|
+
{ name: `archsight-${skillName.slice("aios-".length)}`, requireMatchingFrontmatter: true },
|
|
336
|
+
...(skillAliases[skillName] ?? []).map((name) => ({ name, requireMatchingFrontmatter: false }))
|
|
337
|
+
];
|
|
338
|
+
|
|
339
|
+
for (const { name: legacyName, requireMatchingFrontmatter } of legacyNames) {
|
|
340
|
+
const legacyDir = path.join(targetRoot, legacyName);
|
|
341
|
+
const legacySkillFile = path.join(legacyDir, "SKILL.md");
|
|
342
|
+
if (!(await exists(legacySkillFile))) {
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const legacySkill = await fs.readFile(legacySkillFile, "utf8");
|
|
347
|
+
if (requireMatchingFrontmatter && !legacySkill.includes(`name: ${legacyName}`)) {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
await fs.rm(legacyDir, { recursive: true, force: true });
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async function installWorkflowsTo(targetRoot, workflowPaths) {
|
|
357
|
+
await ensureDir(targetRoot);
|
|
358
|
+
|
|
359
|
+
await copyFileIfExists(
|
|
360
|
+
path.join(repoRoot, "workflows", "README.md"),
|
|
361
|
+
path.join(targetRoot, "README.md")
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
for (const workflowPath of workflowPaths) {
|
|
365
|
+
await copyFileIfExists(
|
|
366
|
+
path.join(repoRoot, workflowPath),
|
|
367
|
+
path.join(targetRoot, path.basename(workflowPath))
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function userInstructionBlock(contentRoot) {
|
|
373
|
+
const p = (value) => value.replaceAll("\\", "/");
|
|
374
|
+
return [
|
|
375
|
+
managedStart,
|
|
376
|
+
"# ArchSight AIOS",
|
|
377
|
+
"",
|
|
378
|
+
"ArchSight AIOS Gemini support assets are installed at:",
|
|
379
|
+
"",
|
|
380
|
+
`- ${p(contentRoot)}`,
|
|
381
|
+
"",
|
|
382
|
+
"When a task matches ArchSight AIOS, read the relevant installed assets before answering:",
|
|
383
|
+
"",
|
|
384
|
+
`- Skills: ${p(path.join(contentRoot, "skills"))}`,
|
|
385
|
+
`- Workflows: ${p(path.join(contentRoot, "workflows"))}`,
|
|
386
|
+
`- Runtime routing: ${p(path.join(contentRoot, "runtime"))}`,
|
|
387
|
+
`- Project template: ${p(path.join(contentRoot, "templates", "project-ai"))}`,
|
|
388
|
+
"",
|
|
389
|
+
"Use enabled `aios-*` skills for architecture review, design review, delivery planning, code review, runtime design, controlled execution, and building knowledge when the project profile or task requires it.",
|
|
390
|
+
"Keep Agent, Skill, Workflow, and Runtime boundaries separate.",
|
|
391
|
+
"Hermes, Feishu, and other runtime adapters are optional; do not assume they are enabled unless the project says so.",
|
|
392
|
+
"Do not claim code changes, tests, builds, or deployments were completed unless verified in the bound project workspace.",
|
|
393
|
+
managedEnd,
|
|
394
|
+
""
|
|
395
|
+
].join("\n");
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function projectInstructionBlock() {
|
|
399
|
+
return [
|
|
400
|
+
managedStart,
|
|
401
|
+
"## ArchSight AIOS",
|
|
402
|
+
"",
|
|
403
|
+
"本项目接入 ArchSight AIOS 作为补充治理层,不替代本项目已有通用 AI 编码规则。",
|
|
404
|
+
"",
|
|
405
|
+
"当任务涉及 Agent 路由、Skill 选择、Workflow、交付验证、AI Runtime、Code Review,或项目明确启用的 BIM / IFC / 建筑行业 profile 时,先阅读:",
|
|
406
|
+
"",
|
|
407
|
+
"- `.ai/ARCHSIGHT_AIOS_RULES.md`",
|
|
408
|
+
"- `.ai/project-context.md`",
|
|
409
|
+
"- `.ai/agent-routing.md`",
|
|
410
|
+
"- `.ai/skills.md`",
|
|
411
|
+
"- `.ai/workflows.md`",
|
|
412
|
+
"- `.ai/profiles/*.md`(如当前项目启用了 profile)",
|
|
413
|
+
"",
|
|
414
|
+
"当前项目事实、根目录工具入口文件和 `AI_CODING_RULES.md` 优先;`.ai/ARCHSIGHT_AIOS_RULES.md` 只补充 AIOS 专属规则。接入 AIOS 不代表项目属于 ArchSightLabs,也不要求使用 Hermes、飞书或其他特定运行平台。",
|
|
415
|
+
managedEnd,
|
|
416
|
+
""
|
|
417
|
+
].join("\n");
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function containsAiosReference(content) {
|
|
421
|
+
return [
|
|
422
|
+
managedStart,
|
|
423
|
+
"ArchSight AIOS",
|
|
424
|
+
"archsight-aios",
|
|
425
|
+
"ARCHSIGHT_AIOS_RULES.md",
|
|
426
|
+
".ai/ARCHSIGHT_AIOS_RULES.md"
|
|
427
|
+
].some((needle) => content.includes(needle));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async function resolveInitProjectMode(requestedMode, targetRoot, rootInstructionFiles, aiFiles) {
|
|
431
|
+
if (requestedMode !== "auto") {
|
|
432
|
+
return requestedMode;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const existingRootFiles = [];
|
|
436
|
+
let hasAiosReference = false;
|
|
437
|
+
|
|
438
|
+
for (const fileName of rootInstructionFiles) {
|
|
439
|
+
const filePath = path.join(targetRoot, fileName);
|
|
440
|
+
if (await exists(filePath)) {
|
|
441
|
+
existingRootFiles.push(fileName);
|
|
442
|
+
hasAiosReference ||= containsAiosReference(await readTextIfExists(filePath));
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
for (const fileName of aiFiles) {
|
|
447
|
+
const filePath = path.join(targetRoot, fileName);
|
|
448
|
+
if (await exists(filePath)) {
|
|
449
|
+
hasAiosReference = true;
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (existingRootFiles.length > 0 || hasAiosReference) {
|
|
455
|
+
return "linked";
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return "full";
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
async function upsertManagedBlock(filePath, block) {
|
|
462
|
+
await ensureDir(path.dirname(filePath));
|
|
463
|
+
const current = (await exists(filePath)) ? await fs.readFile(filePath, "utf8") : "";
|
|
464
|
+
const startIndex = current.indexOf(managedStart);
|
|
465
|
+
const endIndex = current.indexOf(managedEnd);
|
|
466
|
+
|
|
467
|
+
let next;
|
|
468
|
+
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
|
|
469
|
+
const before = current.slice(0, startIndex).trimEnd();
|
|
470
|
+
const after = current.slice(endIndex + managedEnd.length).trimStart();
|
|
471
|
+
next = [before, block.trimEnd(), after].filter(Boolean).join("\n\n") + "\n";
|
|
472
|
+
} else {
|
|
473
|
+
next = `${current.trimEnd()}${current.trimEnd() ? "\n\n" : ""}${block}`;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
await fs.writeFile(filePath, next, "utf8");
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
async function install(options) {
|
|
480
|
+
if (options.scope !== "user") {
|
|
481
|
+
throw new Error("Only --scope user is supported in this release.");
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const validTargets = new Set(["codex", "agents", "gemini", "antigravity", "all"]);
|
|
485
|
+
if (!validTargets.has(options.target)) {
|
|
486
|
+
throw new Error(`Unsupported target: ${options.target}`);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const skillNames = await listAiosSkills();
|
|
490
|
+
const workflowPaths = await listAiosWorkflowPaths();
|
|
491
|
+
const targets = options.target === "all"
|
|
492
|
+
? ["codex", "gemini", "antigravity"]
|
|
493
|
+
: [options.target];
|
|
494
|
+
|
|
495
|
+
const installed = [];
|
|
496
|
+
|
|
497
|
+
if (targets.includes("codex")) {
|
|
498
|
+
const codexSkillsRoot = path.join(home, ".codex", "skills");
|
|
499
|
+
await removeLegacySkillDirs(codexSkillsRoot, skillNames);
|
|
500
|
+
await installSkillsTo(codexSkillsRoot, skillNames);
|
|
501
|
+
await installWorkflowsTo(path.join(home, ".codex", "workflows", "aios"), workflowPaths);
|
|
502
|
+
installed.push("codex skills", "codex workflows");
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (targets.includes("agents")) {
|
|
506
|
+
const sharedSkillsRoot = path.join(home, ".agents", "skills");
|
|
507
|
+
await removeLegacySkillDirs(sharedSkillsRoot, skillNames);
|
|
508
|
+
await installSkillsTo(sharedSkillsRoot, skillNames);
|
|
509
|
+
await installWorkflowsTo(path.join(home, ".agents", "workflows", "aios"), workflowPaths);
|
|
510
|
+
installed.push("shared agent skills", "shared agent workflows");
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (targets.includes("gemini")) {
|
|
514
|
+
const contentRoot = await syncGeminiContent();
|
|
515
|
+
await upsertManagedBlock(path.join(home, ".gemini", "GEMINI.md"), userInstructionBlock(contentRoot));
|
|
516
|
+
installed.push("gemini user instructions", "gemini support assets");
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (targets.includes("antigravity")) {
|
|
520
|
+
const installLegacy = await exists(antigravityLegacyRoot());
|
|
521
|
+
const installPlugin = !installLegacy || await hasAntigravity2Config();
|
|
522
|
+
|
|
523
|
+
if (installLegacy) {
|
|
524
|
+
await installAntigravityLegacy(skillNames);
|
|
525
|
+
installed.push("antigravity 1.x legacy skills");
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (installPlugin) {
|
|
529
|
+
await installAntigravityPlugin(skillNames);
|
|
530
|
+
installed.push("antigravity 2.x plugin");
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
console.log(`Installed: ${installed.join(", ")}`);
|
|
535
|
+
console.log(`Skills: ${skillNames.join(", ")}`);
|
|
536
|
+
console.log(`Workflows: ${workflowPaths.map((workflowPath) => path.basename(workflowPath)).join(", ")}`);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async function doctor() {
|
|
540
|
+
const manifest = await readManifest();
|
|
541
|
+
const geminiRoot = geminiContentRoot();
|
|
542
|
+
const agentIds = new Set(manifest.agents.map((agent) => agent.id));
|
|
543
|
+
const skillIds = new Set(manifest.skills.map((skill) => skill.id));
|
|
544
|
+
const workflowIds = new Set(manifest.workflows.map((workflow) => workflow.id));
|
|
545
|
+
const checks = [];
|
|
546
|
+
|
|
547
|
+
async function check(label, targetPath) {
|
|
548
|
+
const ok = await exists(targetPath);
|
|
549
|
+
checks.push({ label, targetPath, ok });
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
async function checkContains(label, filePath, needle) {
|
|
553
|
+
const ok = await fileContains(filePath, needle);
|
|
554
|
+
checks.push({ label, targetPath: `${filePath} contains ${needle}`, ok });
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function checkCondition(label, ok, detail) {
|
|
558
|
+
checks.push({ label, targetPath: detail, ok });
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const manifestPath = path.join(repoRoot, "runtime", "archsight-aios.manifest.json");
|
|
562
|
+
const skillRoutingPath = path.join(repoRoot, "runtime", "skill-routing.md");
|
|
563
|
+
const packageJson = await readJson(path.join(repoRoot, "package.json"));
|
|
564
|
+
|
|
565
|
+
await check("manifest", manifestPath);
|
|
566
|
+
checkCondition("manifest schema", manifest.schema === 1, "schema === 1");
|
|
567
|
+
checkCondition("manifest name", manifest.name === "archsight-aios", "name === archsight-aios");
|
|
568
|
+
checkCondition("package name", packageJson.name === "@archsight/aios", "name === @archsight/aios");
|
|
569
|
+
checkCondition("package bin", packageJson.bin?.["archsight-aios"] === "./bin/archsight-aios.mjs", "bin.archsight-aios");
|
|
570
|
+
checkCondition("codex workflows target", manifest.installTargets?.codexWorkflows === "~/.codex/workflows/aios", "codexWorkflows");
|
|
571
|
+
checkCondition("antigravity plugin target", manifest.installTargets?.antigravityPlugin === "~/.gemini/config/plugins/archsight-aios", "antigravityPlugin");
|
|
572
|
+
checkCondition("antigravity legacy skills target", manifest.installTargets?.antigravityLegacySkills === "~/.gemini/antigravity/skills", "antigravityLegacySkills");
|
|
573
|
+
|
|
574
|
+
for (const agent of manifest.agents) {
|
|
575
|
+
await check(`agent source ${agent.id}`, path.join(repoRoot, agent.sourcePath));
|
|
576
|
+
await check(`agent runtime prompt ${agent.id}`, path.join(repoRoot, agent.runtimePromptPath));
|
|
577
|
+
for (const fileName of ["role.md", "responsibilities.md", "constraints.md", "workflow.md", "system-prompt.md"]) {
|
|
578
|
+
await check(`agent file ${agent.id}/${fileName}`, path.join(repoRoot, agent.sourcePath, fileName));
|
|
579
|
+
}
|
|
580
|
+
await checkContains(`agent routing mentions ${agent.displayName}`, path.join(repoRoot, "runtime", "agent-routing.md"), agent.displayName);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
for (const skill of manifest.skills) {
|
|
584
|
+
await check(`skill ${skill.id}`, path.join(repoRoot, skill.path));
|
|
585
|
+
await check(`skill openai config ${skill.id}`, path.join(repoRoot, skill.openaiConfigPath));
|
|
586
|
+
await checkContains(`skill frontmatter ${skill.id}`, path.join(repoRoot, skill.path), `name: ${skill.id}`);
|
|
587
|
+
await checkContains(`skill routing mentions ${skill.id}`, skillRoutingPath, skill.id);
|
|
588
|
+
checkCondition(`skill agent exists ${skill.id}`, agentIds.has(skill.primaryAgent), skill.primaryAgent);
|
|
589
|
+
checkCondition(`skill workflow exists ${skill.id}`, workflowIds.has(skill.defaultWorkflow), skill.defaultWorkflow);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
for (const workflow of manifest.workflows) {
|
|
593
|
+
await check(`workflow ${workflow.id}`, path.join(repoRoot, workflow.path));
|
|
594
|
+
await checkContains(`workflow index mentions ${workflow.id}`, path.join(repoRoot, "workflows", "README.md"), workflow.path.replace("workflows/", ""));
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
for (const route of manifest.routes) {
|
|
598
|
+
checkCondition(`route skill exists ${route.taskType}`, skillIds.has(route.skill), route.skill);
|
|
599
|
+
checkCondition(`route agent exists ${route.taskType}`, agentIds.has(route.agent), route.agent);
|
|
600
|
+
checkCondition(`route workflow exists ${route.taskType}`, workflowIds.has(route.workflow), route.workflow);
|
|
601
|
+
await checkContains(`routing table skill ${route.skill}`, skillRoutingPath, route.skill);
|
|
602
|
+
await checkContains(`routing table agent ${route.agent}`, skillRoutingPath, routeAgentName(manifest, route.agent));
|
|
603
|
+
await checkContains(`routing table workflow ${route.workflow}`, skillRoutingPath, route.workflow);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
for (const fileName of manifest.projectTemplate.requiredFiles) {
|
|
607
|
+
await check(`project template ${fileName}`, path.join(repoRoot, manifest.projectTemplate.path, fileName));
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
for (const profile of manifest.projectProfiles ?? []) {
|
|
611
|
+
await check(`project profile ${profile.id}`, path.join(repoRoot, profile.path));
|
|
612
|
+
for (const fileName of profile.requiredFiles ?? []) {
|
|
613
|
+
await check(`project profile ${profile.id}/${fileName}`, path.join(repoRoot, profile.path, fileName));
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
for (const fileName of manifest.requiredAssets ?? []) {
|
|
618
|
+
await check(`required asset ${fileName}`, path.join(repoRoot, fileName));
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (manifest.hermes) {
|
|
622
|
+
await check("hermes registry", path.join(repoRoot, manifest.hermes.registryPath));
|
|
623
|
+
await check("hermes sync policy", path.join(repoRoot, manifest.hermes.syncPolicyPath));
|
|
624
|
+
await check("hermes sync record template", path.join(repoRoot, manifest.hermes.syncRecordTemplatePath));
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
await check("gemini support assets", geminiRoot);
|
|
628
|
+
await check("gemini support skills", path.join(geminiRoot, "skills"));
|
|
629
|
+
await check("gemini support workflows", path.join(geminiRoot, "workflows"));
|
|
630
|
+
await check("gemini support manifest", path.join(geminiRoot, "runtime", "archsight-aios.manifest.json"));
|
|
631
|
+
await check("gemini support governance", path.join(geminiRoot, "governance"));
|
|
632
|
+
await check("gemini support delivery", path.join(geminiRoot, "delivery"));
|
|
633
|
+
await check("gemini support memory", path.join(geminiRoot, "memory"));
|
|
634
|
+
await check("codex skills root", path.join(home, ".codex", "skills"));
|
|
635
|
+
await check("codex workflows root", path.join(home, ".codex", "workflows", "aios"));
|
|
636
|
+
await check("gemini instructions", path.join(home, ".gemini", "GEMINI.md"));
|
|
637
|
+
await checkContains("gemini managed block", path.join(home, ".gemini", "GEMINI.md"), managedStart);
|
|
638
|
+
|
|
639
|
+
const useAntigravityLegacy = await exists(antigravityLegacyRoot());
|
|
640
|
+
const useAntigravityPlugin = !useAntigravityLegacy || await hasAntigravity2Config();
|
|
641
|
+
if (useAntigravityLegacy) {
|
|
642
|
+
await check("antigravity 1.x legacy skills root", antigravityLegacySkillsRoot());
|
|
643
|
+
}
|
|
644
|
+
if (useAntigravityPlugin) {
|
|
645
|
+
await check("antigravity 2.x plugin root", antigravityPluginRoot());
|
|
646
|
+
await checkContains("antigravity 2.x plugin manifest", path.join(antigravityPluginRoot(), "plugin.json"), antigravityPluginName);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
for (const skill of manifest.skills) {
|
|
650
|
+
const skillName = skill.id;
|
|
651
|
+
const sourceDir = expectedSkillDir(skill);
|
|
652
|
+
await check(`gemini support skill ${skillName}`, path.join(geminiRoot, sourceDir, "SKILL.md"));
|
|
653
|
+
await check(`codex skill ${skillName}`, path.join(home, ".codex", "skills", skillName, "SKILL.md"));
|
|
654
|
+
if (useAntigravityLegacy) {
|
|
655
|
+
await check(`antigravity 1.x legacy skill ${skillName}`, path.join(antigravityLegacySkillsRoot(), skillName, "SKILL.md"));
|
|
656
|
+
}
|
|
657
|
+
if (useAntigravityPlugin) {
|
|
658
|
+
await check(`antigravity 2.x plugin skill ${skillName}`, path.join(antigravityPluginRoot(), "skills", skillName, "SKILL.md"));
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
for (const workflow of manifest.workflows) {
|
|
663
|
+
const workflowFileName = path.basename(workflow.path);
|
|
664
|
+
await check(`codex workflow ${workflow.id}`, path.join(home, ".codex", "workflows", "aios", workflowFileName));
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
const failed = checks.filter((item) => !item.ok);
|
|
668
|
+
for (const item of checks) {
|
|
669
|
+
console.log(`${item.ok ? "OK " : "MISS"} ${item.label}: ${item.targetPath}`);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
if (failed.length > 0) {
|
|
673
|
+
process.exitCode = 1;
|
|
674
|
+
console.error(`Doctor failed: ${failed.length} missing check(s).`);
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
console.log("Doctor passed.");
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
async function initProject(options) {
|
|
682
|
+
const templateRoot = path.join(repoRoot, "templates", "project-ai");
|
|
683
|
+
const targetRoot = path.resolve(options.cwd);
|
|
684
|
+
const manifest = await readManifest();
|
|
685
|
+
const rootInstructionFiles = [
|
|
686
|
+
"AGENTS.md",
|
|
687
|
+
"AI_CODING_RULES.md",
|
|
688
|
+
"CLAUDE.md",
|
|
689
|
+
"GEMINI.md"
|
|
690
|
+
];
|
|
691
|
+
const linkedInstructionFiles = [
|
|
692
|
+
"AGENTS.md",
|
|
693
|
+
"CLAUDE.md",
|
|
694
|
+
"GEMINI.md"
|
|
695
|
+
];
|
|
696
|
+
const aiFiles = [
|
|
697
|
+
path.join(".ai", "ARCHSIGHT_AIOS_RULES.md"),
|
|
698
|
+
path.join(".ai", "project-context.md"),
|
|
699
|
+
path.join(".ai", "agent-routing.md"),
|
|
700
|
+
path.join(".ai", "skills.md"),
|
|
701
|
+
path.join(".ai", "workflows.md")
|
|
702
|
+
];
|
|
703
|
+
const mode = await resolveInitProjectMode(options.mode ?? "auto", targetRoot, rootInstructionFiles, aiFiles);
|
|
704
|
+
const files = mode === "ai-only"
|
|
705
|
+
? aiFiles
|
|
706
|
+
: mode === "linked"
|
|
707
|
+
? aiFiles
|
|
708
|
+
: mode === "full"
|
|
709
|
+
? [...rootInstructionFiles, ...aiFiles]
|
|
710
|
+
: undefined;
|
|
711
|
+
|
|
712
|
+
if (!files) {
|
|
713
|
+
throw new Error(`Unsupported init mode: ${mode}`);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
await ensureDir(targetRoot);
|
|
717
|
+
|
|
718
|
+
for (const fileName of files) {
|
|
719
|
+
const src = path.join(templateRoot, fileName);
|
|
720
|
+
const dest = path.join(targetRoot, fileName);
|
|
721
|
+
assertInside(dest, targetRoot);
|
|
722
|
+
if (await exists(dest)) {
|
|
723
|
+
console.log(`SKIP existing ${dest}`);
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
await ensureDir(path.dirname(dest));
|
|
727
|
+
await fs.copyFile(src, dest);
|
|
728
|
+
console.log(`CREATE ${dest}`);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
if (mode === "linked") {
|
|
732
|
+
const block = projectInstructionBlock();
|
|
733
|
+
for (const fileName of linkedInstructionFiles) {
|
|
734
|
+
const src = path.join(templateRoot, fileName);
|
|
735
|
+
const dest = path.join(targetRoot, fileName);
|
|
736
|
+
assertInside(dest, targetRoot);
|
|
737
|
+
if (!(await exists(dest))) {
|
|
738
|
+
await ensureDir(path.dirname(dest));
|
|
739
|
+
await fs.copyFile(src, dest);
|
|
740
|
+
console.log(`CREATE ${dest}`);
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
await upsertManagedBlock(dest, block);
|
|
744
|
+
console.log(`LINK ${dest}`);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
if (options.profile) {
|
|
749
|
+
const profile = manifest.projectProfiles?.find((item) => item.id === options.profile);
|
|
750
|
+
if (!profile) {
|
|
751
|
+
throw new Error(`Unsupported project profile: ${options.profile}`);
|
|
752
|
+
}
|
|
753
|
+
await copyTreeMissing(path.join(repoRoot, profile.path), targetRoot);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
async function validateProjectTemplate(options) {
|
|
758
|
+
const manifest = await readManifest();
|
|
759
|
+
const createdTemp = options.temp;
|
|
760
|
+
const targetRoot = createdTemp
|
|
761
|
+
? await fs.mkdtemp(path.join(os.tmpdir(), "archsight-aios-project-"))
|
|
762
|
+
: path.resolve(options.cwd);
|
|
763
|
+
|
|
764
|
+
if (createdTemp) {
|
|
765
|
+
await fs.writeFile(path.join(targetRoot, "README.md"), "# ArchSight AIOS local validation\n", "utf8");
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
await initProject({ cwd: targetRoot, profile: options.profile });
|
|
769
|
+
|
|
770
|
+
const checks = [];
|
|
771
|
+
async function check(label, targetPath) {
|
|
772
|
+
const ok = await exists(targetPath);
|
|
773
|
+
checks.push({ label, targetPath, ok });
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
for (const fileName of manifest.projectTemplate.requiredFiles) {
|
|
777
|
+
await check(`template output ${fileName}`, path.join(targetRoot, fileName));
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
await check("project skills doc", path.join(targetRoot, ".ai", "skills.md"));
|
|
781
|
+
await check("project workflows doc", path.join(targetRoot, ".ai", "workflows.md"));
|
|
782
|
+
|
|
783
|
+
const skillsDoc = await fs.readFile(path.join(targetRoot, ".ai", "skills.md"), "utf8");
|
|
784
|
+
const workflowsDoc = await fs.readFile(path.join(targetRoot, ".ai", "workflows.md"), "utf8");
|
|
785
|
+
|
|
786
|
+
for (const skill of manifest.skills) {
|
|
787
|
+
checks.push({
|
|
788
|
+
label: `project mentions skill ${skill.id}`,
|
|
789
|
+
targetPath: ".ai/skills.md",
|
|
790
|
+
ok: skillsDoc.includes(skill.id)
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
for (const workflow of manifest.workflows) {
|
|
795
|
+
checks.push({
|
|
796
|
+
label: `project mentions workflow ${workflow.id}`,
|
|
797
|
+
targetPath: ".ai/workflows.md",
|
|
798
|
+
ok: workflowsDoc.includes(workflow.id)
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
for (const [label, fileName] of [
|
|
803
|
+
["project AGENTS uses AIOS", "AGENTS.md"],
|
|
804
|
+
["project coding rules uses AIOS", "AI_CODING_RULES.md"],
|
|
805
|
+
["project AIOS rules uses AIOS", path.join(".ai", "ARCHSIGHT_AIOS_RULES.md")],
|
|
806
|
+
["project CLAUDE uses AIOS", "CLAUDE.md"],
|
|
807
|
+
["project GEMINI uses AIOS", "GEMINI.md"],
|
|
808
|
+
["project context uses AIOS", path.join(".ai", "project-context.md")]
|
|
809
|
+
]) {
|
|
810
|
+
const content = await fs.readFile(path.join(targetRoot, fileName), "utf8");
|
|
811
|
+
const legacyAiOsText = ["AI", "OS"].join(" ");
|
|
812
|
+
checks.push({
|
|
813
|
+
label,
|
|
814
|
+
targetPath: fileName,
|
|
815
|
+
ok: content.includes("AIOS") && !content.includes(legacyAiOsText)
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if (options.profile) {
|
|
820
|
+
const profile = manifest.projectProfiles?.find((item) => item.id === options.profile);
|
|
821
|
+
if (!profile) {
|
|
822
|
+
throw new Error(`Unsupported project profile: ${options.profile}`);
|
|
823
|
+
}
|
|
824
|
+
for (const fileName of profile.requiredFiles ?? []) {
|
|
825
|
+
await check(`profile output ${options.profile}/${fileName}`, path.join(targetRoot, fileName));
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
const failed = checks.filter((item) => !item.ok);
|
|
830
|
+
for (const item of checks) {
|
|
831
|
+
console.log(`${item.ok ? "OK " : "MISS"} ${item.label}: ${item.targetPath}`);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
if (createdTemp) {
|
|
835
|
+
await fs.rm(targetRoot, { recursive: true, force: true });
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
if (failed.length > 0) {
|
|
839
|
+
process.exitCode = 1;
|
|
840
|
+
console.error(`Project template validation failed: ${failed.length} missing check(s).`);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
console.log(`Project template validation passed${createdTemp ? " in a local temporary workspace" : ` for ${targetRoot}`}.`);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
async function validateHermesRegistry() {
|
|
848
|
+
const manifest = await readManifest();
|
|
849
|
+
const registryPath = path.join(repoRoot, manifest.hermes?.registryPath ?? "runtime/hermes/agent-registry.md");
|
|
850
|
+
const registry = await fs.readFile(registryPath, "utf8");
|
|
851
|
+
const checks = [];
|
|
852
|
+
|
|
853
|
+
for (const agent of manifest.agents) {
|
|
854
|
+
checks.push({
|
|
855
|
+
label: `registry mentions ${agent.displayName}`,
|
|
856
|
+
ok: registry.includes(agent.displayName)
|
|
857
|
+
});
|
|
858
|
+
checks.push({
|
|
859
|
+
label: `registry prompt ${agent.runtimePromptPath}`,
|
|
860
|
+
ok: registry.includes(agent.runtimePromptPath)
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
const failed = checks.filter((item) => !item.ok);
|
|
865
|
+
for (const item of checks) {
|
|
866
|
+
console.log(`${item.ok ? "OK " : "MISS"} ${item.label}`);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
if (failed.length > 0) {
|
|
870
|
+
process.exitCode = 1;
|
|
871
|
+
console.error(`Hermes registry validation failed: ${failed.length} issue(s).`);
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
console.log("Hermes registry validation passed.");
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
async function hermesSyncDryRun() {
|
|
879
|
+
const manifest = await readManifest();
|
|
880
|
+
await validateHermesRegistry();
|
|
881
|
+
if (process.exitCode) {
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
console.log("Hermes sync dry-run plan:");
|
|
886
|
+
for (const agent of manifest.agents) {
|
|
887
|
+
const promptPath = path.join(repoRoot, agent.runtimePromptPath);
|
|
888
|
+
const prompt = await fs.readFile(promptPath, "utf8");
|
|
889
|
+
console.log(`SYNC ${agent.displayName}: ${agent.runtimePromptPath} (${prompt.length} bytes)`);
|
|
890
|
+
}
|
|
891
|
+
console.log("Dry-run only: no Hermes API calls were made.");
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
async function hermesDetectDrift() {
|
|
895
|
+
const manifest = await readManifest();
|
|
896
|
+
const checks = [];
|
|
897
|
+
|
|
898
|
+
for (const agent of manifest.agents) {
|
|
899
|
+
const promptPath = path.join(repoRoot, agent.runtimePromptPath);
|
|
900
|
+
const sourcePath = path.join(repoRoot, agent.sourcePath);
|
|
901
|
+
const promptExists = await exists(promptPath);
|
|
902
|
+
const sourceExists = await exists(sourcePath);
|
|
903
|
+
checks.push({
|
|
904
|
+
label: `${agent.displayName} source/runtime present`,
|
|
905
|
+
ok: promptExists && sourceExists
|
|
906
|
+
});
|
|
907
|
+
if (promptExists) {
|
|
908
|
+
const prompt = await fs.readFile(promptPath, "utf8");
|
|
909
|
+
checks.push({
|
|
910
|
+
label: `${agent.displayName} runtime prompt is non-empty and named`,
|
|
911
|
+
ok: prompt.trim().length > 100 && prompt.includes(agent.displayName)
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
const failed = checks.filter((item) => !item.ok);
|
|
917
|
+
for (const item of checks) {
|
|
918
|
+
console.log(`${item.ok ? "OK " : "MISS"} ${item.label}`);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
if (failed.length > 0) {
|
|
922
|
+
process.exitCode = 1;
|
|
923
|
+
console.error(`Hermes drift detection failed: ${failed.length} issue(s).`);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
console.log("Hermes drift detection passed for repository-managed runtime prompts.");
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
async function main() {
|
|
931
|
+
const options = parseArgs(process.argv.slice(2));
|
|
932
|
+
if (!options.command || options.help) {
|
|
933
|
+
console.log(usage());
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (options.command === "install") {
|
|
938
|
+
await install(options);
|
|
939
|
+
} else if (options.command === "doctor") {
|
|
940
|
+
await doctor();
|
|
941
|
+
} else if (options.command === "init") {
|
|
942
|
+
await initProject(options);
|
|
943
|
+
} else if (options.command === "validate") {
|
|
944
|
+
await validateProjectTemplate(options);
|
|
945
|
+
} else if (options.command === "hermes:validate") {
|
|
946
|
+
await validateHermesRegistry();
|
|
947
|
+
} else if (options.command === "hermes:sync-dry-run") {
|
|
948
|
+
await hermesSyncDryRun();
|
|
949
|
+
} else if (options.command === "hermes:detect-drift") {
|
|
950
|
+
await hermesDetectDrift();
|
|
951
|
+
} else {
|
|
952
|
+
throw new Error(`Unknown command: ${options.command}`);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
main().catch((error) => {
|
|
957
|
+
console.error(error.message);
|
|
958
|
+
process.exitCode = 1;
|
|
959
|
+
});
|