@archsight/aios 1.2.0 → 1.3.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.
Files changed (84) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +35 -0
  3. package/OPENCODE.md +23 -0
  4. package/README.md +64 -31
  5. package/RELEASE_NOTES.md +25 -0
  6. package/agents/README.md +6 -3
  7. package/agents/daedalus/system-prompt.md +2 -0
  8. package/agents/hestia/constraints.md +7 -0
  9. package/agents/hestia/responsibilities.md +7 -0
  10. package/agents/hestia/role.md +12 -0
  11. package/agents/hestia/system-prompt.md +23 -0
  12. package/agents/hestia/workflow.md +8 -0
  13. package/agents/plutus/constraints.md +7 -0
  14. package/agents/plutus/responsibilities.md +7 -0
  15. package/agents/plutus/role.md +12 -0
  16. package/agents/plutus/system-prompt.md +24 -0
  17. package/agents/plutus/workflow.md +8 -0
  18. package/agents/themis/constraints.md +7 -0
  19. package/agents/themis/responsibilities.md +7 -0
  20. package/agents/themis/role.md +12 -0
  21. package/agents/themis/system-prompt.md +24 -0
  22. package/agents/themis/workflow.md +8 -0
  23. package/bin/archsight-aios.mjs +555 -24
  24. package/docs/PUBLIC_DISCOVERY.md +16 -2
  25. package/docs/business-expert-guide.md +5 -3
  26. package/docs/glossary.md +11 -3
  27. package/docs/quickstart.md +18 -4
  28. package/gemini-extension.json +1 -1
  29. package/package.json +17 -6
  30. package/prompts/README.md +12 -0
  31. package/prompts/evaluation-policy.md +70 -0
  32. package/prompts/evaluations/engineering-business-basic-advisory-validation-2026-06-16.md +87 -0
  33. package/prompts/evaluations/engineering-business-basic-fixtures.json +375 -0
  34. package/prompts/evaluations/engineering-business-basic-model-output.example.json +179 -0
  35. package/prompts/evaluations/engineering-business-basic-prompts-2026-06-16.md +205 -0
  36. package/prompts/evaluations/engineering-business-basic-scorecard.json +238 -0
  37. package/prompts/evaluations/engineering-business-public-advisory-fixtures.json +422 -0
  38. package/prompts/evaluations/public-advisory-md/01-technical-bid.md +63 -0
  39. package/prompts/evaluations/public-advisory-md/02-contract.md +61 -0
  40. package/prompts/evaluations/public-advisory-md/03-daily.md +69 -0
  41. package/prompts/evaluations/public-advisory-md/04-meeting.md +48 -0
  42. package/prompts/evaluations/public-advisory-md/05-variation.md +63 -0
  43. package/prompts/evaluations/public-advisory-md/06-scheme.md +60 -0
  44. package/prompts/failure-cases.md +5 -1
  45. package/prompts/prompt-registry.md +10 -0
  46. package/runtime/agent-routing.md +36 -8
  47. package/runtime/archsight-aios.manifest.json +104 -40
  48. package/runtime/hermes/agent-registry.md +3 -0
  49. package/runtime/hermes/workspace-binding.md +3 -0
  50. package/runtime/skill-routing.md +12 -7
  51. package/scripts/analyze-prompt-run-results.mjs +187 -0
  52. package/scripts/build-prompt-run-pack.mjs +248 -0
  53. package/scripts/validate-prompt-fixtures.mjs +225 -0
  54. package/scripts/validate-prompt-model-outputs.mjs +201 -0
  55. package/scripts/validate-prompt-run-results.mjs +259 -0
  56. package/scripts/validate-prompt-scorecard.mjs +133 -0
  57. package/scripts/validate-skills.mjs +6 -2
  58. package/skills/README.md +3 -0
  59. package/skills/aios-commercial-contract/SKILL.md +18 -0
  60. package/skills/aios-commercial-contract/prompts/basic-prompt.md +83 -0
  61. package/skills/aios-commercial-tender/SKILL.md +18 -0
  62. package/skills/aios-commercial-tender/prompts/basic-prompt.md +94 -0
  63. package/skills/aios-commercial-variation/SKILL.md +18 -0
  64. package/skills/aios-commercial-variation/prompts/basic-prompt.md +99 -0
  65. package/skills/aios-construction-daily/SKILL.md +18 -0
  66. package/skills/aios-construction-daily/prompts/basic-prompt.md +76 -0
  67. package/skills/aios-construction-meeting/SKILL.md +18 -0
  68. package/skills/aios-construction-meeting/prompts/basic-prompt.md +78 -0
  69. package/skills/aios-construction-scheme/SKILL.md +18 -0
  70. package/skills/aios-construction-scheme/prompts/basic-prompt.md +90 -0
  71. package/skills/aios-prompt-compare/SKILL.md +178 -0
  72. package/skills/aios-prompt-compare/agents/openai.yaml +4 -0
  73. package/skills/engineering-business-starter-kit.md +109 -0
  74. package/templates/README.md +16 -2
  75. package/templates/project-ai/.ai/ARCHSIGHT_AIOS_RULES.md +5 -4
  76. package/templates/project-ai/.ai/agent-routing.md +3 -1
  77. package/templates/project-ai/.ai/profile-detection.md +24 -0
  78. package/templates/project-ai/.ai/project-context.md +4 -1
  79. package/templates/project-ai/.ai/skills.md +25 -20
  80. package/templates/project-ai/AGENTS.md +6 -5
  81. package/templates/project-ai/AI_CODING_RULES.md +1 -1
  82. package/templates/project-ai/CLAUDE.md +6 -5
  83. package/templates/project-ai/GEMINI.md +6 -5
  84. package/templates/project-ai/OPENCODE.md +26 -0
@@ -34,7 +34,8 @@ const assetDirs = [
34
34
  "vision",
35
35
  "docs"
36
36
  ];
37
- const assetFiles = ["README.md", "AI_CODING_RULES.md", "AGENTS.md", "CLAUDE.md", "GEMINI.md"];
37
+ const assetFiles = ["README.md", "AI_CODING_RULES.md", "AGENTS.md", "CLAUDE.md", "GEMINI.md", "OPENCODE.md"];
38
+ const skillSupportFiles = ["README.md", "engineering-business-starter-kit.md"];
38
39
  const skillAliases = {
39
40
  "aios-arch": ["aios-architecture-review", "archsight-architecture-review"],
40
41
  "aios-plan": ["aios-delivery-planning", "archsight-delivery-planning"],
@@ -61,16 +62,107 @@ const skillAliases = {
61
62
  "aios-construction-scheme": ["aios-scheme", "archsight-scheme"]
62
63
  };
63
64
 
65
+ const profileDetectionRules = {
66
+ "bim-platform": [
67
+ "bim",
68
+ "ifc",
69
+ "revit",
70
+ "cad",
71
+ "dynamo",
72
+ "autocad",
73
+ "rvt",
74
+ "dwg",
75
+ "dxf",
76
+ "模型",
77
+ "构件",
78
+ "族",
79
+ "楼层",
80
+ "建模",
81
+ "模型质检"
82
+ ],
83
+ "construction-vision": [
84
+ "yolo",
85
+ "sam",
86
+ "segment anything",
87
+ "segmentation",
88
+ "detect",
89
+ "detection",
90
+ "图像",
91
+ "视频",
92
+ "照片",
93
+ "裂缝",
94
+ "焊缝",
95
+ "点云",
96
+ "深度估计",
97
+ "巡检",
98
+ "缺陷",
99
+ "标注",
100
+ "数据集",
101
+ "jpg",
102
+ "png",
103
+ "mp4"
104
+ ],
105
+ "rag-knowledge": [
106
+ "rag",
107
+ "graphrag",
108
+ "knowledge graph",
109
+ "知识库",
110
+ "知识图谱",
111
+ "规范",
112
+ "条文",
113
+ "审图",
114
+ "标准",
115
+ "检索",
116
+ "向量",
117
+ "embedding",
118
+ "问答",
119
+ "评估问题"
120
+ ]
121
+ };
122
+
123
+ const skillDetectionRules = {
124
+ "aios-arch": ["架构", "服务边界", "技术选型", "系统设计"],
125
+ "aios-design": ["界面", "ui", "ux", "工作台", "交互", "原型"],
126
+ "aios-plan": ["计划", "排期", "里程碑", "任务拆解", "交付"],
127
+ "aios-review": ["code review", "代码审查", "安全审查", "技术债"],
128
+ "aios-knowledge": ["bim", "ifc", "规范", "审图", "条文", "知识结构化"],
129
+ "aios-structural": ["结构", "荷载", "挠度", "fem", "有限元", "计算书"],
130
+ "aios-runtime": ["rag", "graphrag", "mcp", "tool calling", "memory", "agent runtime"],
131
+ "aios-prompt-compare": ["提示词", "prompt", "对比", "skill 输出", "weak", "basic"],
132
+ "aios-commercial-tender": ["招标", "投标", "技术标", "商务标", "评分", "废标", "招采", "资格"],
133
+ "aios-commercial-contract": ["合同", "协议", "付款", "履约", "违约", "分包", "采购", "结算条款"],
134
+ "aios-construction-daily": ["日报", "周报", "现场记录", "施工日志", "进度", "材料进场", "机械", "劳务"],
135
+ "aios-construction-meeting": ["会议纪要", "例会", "协调会", "专题会", "待办", "责任人"],
136
+ "aios-commercial-variation": ["变更", "签证", "联系单", "索赔", "洽商", "工程量"],
137
+ "aios-construction-scheme": ["施工方案", "专项方案", "危大", "交底", "危险源", "专家论证"]
138
+ };
139
+
140
+ const ignoredDetectionDirs = new Set([
141
+ ".git",
142
+ ".hg",
143
+ ".svn",
144
+ ".ai",
145
+ "node_modules",
146
+ "dist",
147
+ "build",
148
+ "coverage",
149
+ ".next",
150
+ ".nuxt",
151
+ ".venv",
152
+ "venv",
153
+ "__pycache__"
154
+ ]);
155
+
64
156
  function usage() {
65
157
  return [
66
158
  "ArchSight AIOS",
67
159
  "",
68
160
  "Usage:",
69
161
  " archsight-aios help",
70
- " archsight-aios install --target <codex|agents|gemini|antigravity|workbuddy|all> --scope user",
162
+ " archsight-aios install --target <codex|agents|gemini|antigravity|workbuddy|opencode|claude-code|all> --scope user",
71
163
  " archsight-aios doctor",
72
- " archsight-aios init [--cwd <path>] [--mode <auto|full|linked|ai-only>] [--profile <name>]",
73
- " archsight-aios validate [--cwd <path>] [--profile <name>] [--temp]",
164
+ " archsight-aios init [--cwd <path>] [--mode <auto|full|linked|ai-only>] [--profile <auto|none|all|name>]",
165
+ " archsight-aios validate [--cwd <path>] [--profile <auto|none|all|name>] [--temp]",
74
166
  " archsight-aios capability:call --capability <id> --agent <id> --skill <id> --input <json-file>",
75
167
  " archsight-aios hermes:validate",
76
168
  " archsight-aios hermes:sync-dry-run",
@@ -89,6 +181,8 @@ function usage() {
89
181
  " npx @archsight/aios install --target codex --scope user",
90
182
  " npx @archsight/aios install --target agents --scope user",
91
183
  " npx @archsight/aios install --target workbuddy --scope user",
184
+ " npx @archsight/aios install --target opencode --scope user",
185
+ " npx @archsight/aios install --target claude-code --scope user",
92
186
  " npx @archsight/aios init",
93
187
  " npx @archsight/aios validate --temp",
94
188
  " npx @archsight/aios doctor"
@@ -529,7 +623,7 @@ function callMcpStdio({ command, args, cwd, toolName, input, timeoutMs }) {
529
623
  params: {
530
624
  protocolVersion: "2025-06-18",
531
625
  capabilities: {},
532
- clientInfo: { name: "archsight-aios", version: "1.2.0" }
626
+ clientInfo: { name: "archsight-aios", version: "1.3.0" }
533
627
  }
534
628
  };
535
629
  const callTool = {
@@ -671,6 +765,13 @@ async function syncGeminiContent() {
671
765
 
672
766
  async function installSkillsTo(targetRoot, skillNames) {
673
767
  await ensureDir(targetRoot);
768
+ for (const fileName of skillSupportFiles) {
769
+ await copyFileIfExists(
770
+ path.join(repoRoot, "skills", fileName),
771
+ path.join(targetRoot, fileName)
772
+ );
773
+ }
774
+
674
775
  for (const skillName of skillNames) {
675
776
  await copyDir(
676
777
  path.join(repoRoot, "skills", skillName),
@@ -704,6 +805,14 @@ function workBuddySkillsRoot() {
704
805
  return path.join(home, ".workbuddy", "skills");
705
806
  }
706
807
 
808
+ function openCodeSkillsRoot() {
809
+ return path.join(home, ".opencode", "skills");
810
+ }
811
+
812
+ function claudeCodeSkillsRoot() {
813
+ return path.join(home, ".claude", "skills");
814
+ }
815
+
707
816
  async function hasAntigravity2Config() {
708
817
  if (!(await exists(path.join(home, ".gemini", "config")))) {
709
818
  return false;
@@ -804,7 +913,7 @@ function userInstructionBlock(contentRoot) {
804
913
  `- Runtime routing: ${p(path.join(contentRoot, "runtime"))}`,
805
914
  `- Project template: ${p(path.join(contentRoot, "templates", "project-ai"))}`,
806
915
  "",
807
- "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.",
916
+ "Use enabled `aios-*` skills for architecture review, design review, delivery planning, code review, runtime design, controlled execution, and building knowledge when project profile detection or the task requires it.",
808
917
  "Keep Agent, Skill, Workflow, and Runtime boundaries separate.",
809
918
  "Hermes, Feishu, and other runtime adapters are optional; do not assume they are enabled unless the project says so.",
810
919
  "Do not claim code changes, tests, builds, or deployments were completed unless verified in the bound project workspace.",
@@ -820,14 +929,15 @@ function projectInstructionBlock() {
820
929
  "",
821
930
  "本项目接入 ArchSight AIOS 作为补充治理层,不替代本项目已有通用 AI 编码规则。",
822
931
  "",
823
- "当任务涉及 Agent 路由、Skill 选择、Workflow、交付验证、AI Runtime、Code Review,或项目明确启用的 BIM / IFC / 建筑行业 profile 时,先阅读:",
932
+ "当任务涉及 Agent 路由、Skill 选择、Workflow、交付验证、AI Runtime、Code Review,或 AIOS 自动识别 / 用户明确启用的建筑行业 profile 时,先阅读:",
824
933
  "",
825
934
  "- `.ai/ARCHSIGHT_AIOS_RULES.md`",
826
935
  "- `.ai/project-context.md`",
827
936
  "- `.ai/agent-routing.md`",
828
937
  "- `.ai/skills.md`",
829
938
  "- `.ai/workflows.md`",
830
- "- `.ai/profiles/*.md`(如当前项目启用了 profile)",
939
+ "- `.ai/profile-detection.md`",
940
+ "- `.ai/profiles/*.md`(如当前项目自动识别或显式启用了 profile)",
831
941
  "",
832
942
  "当前项目事实、根目录工具入口文件和 `AI_CODING_RULES.md` 优先;`.ai/ARCHSIGHT_AIOS_RULES.md` 只补充 AIOS 专属规则。接入 AIOS 不代表项目属于 ArchSightLabs,也不要求使用 Hermes、飞书或其他特定运行平台。",
833
943
  managedEnd,
@@ -845,6 +955,377 @@ function containsAiosReference(content) {
845
955
  ].some((needle) => content.includes(needle));
846
956
  }
847
957
 
958
+ function normalizeSignal(value) {
959
+ return String(value ?? "").toLowerCase();
960
+ }
961
+
962
+ function confidenceFromScore(score) {
963
+ if (score >= 4) {
964
+ return "high";
965
+ }
966
+ if (score >= 2) {
967
+ return "medium";
968
+ }
969
+ return "low";
970
+ }
971
+
972
+ function scoreDetectionRules(ruleMap, signalText) {
973
+ const normalized = normalizeSignal(signalText);
974
+ return Object.entries(ruleMap)
975
+ .map(([id, keywords]) => {
976
+ const matches = keywords.filter((keyword) => normalized.includes(normalizeSignal(keyword)));
977
+ return {
978
+ id,
979
+ score: matches.length,
980
+ confidence: matches.length > 0 ? confidenceFromScore(matches.length) : "none",
981
+ matches
982
+ };
983
+ })
984
+ .filter((item) => item.score > 0)
985
+ .sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
986
+ }
987
+
988
+ async function listDetectionEntries(root, maxDepth = 2, maxEntries = 500) {
989
+ const entries = [];
990
+
991
+ async function walk(currentRoot, depth) {
992
+ if (entries.length >= maxEntries || depth > maxDepth) {
993
+ return;
994
+ }
995
+
996
+ let dirEntries;
997
+ try {
998
+ dirEntries = await fs.readdir(currentRoot, { withFileTypes: true });
999
+ } catch {
1000
+ return;
1001
+ }
1002
+
1003
+ dirEntries.sort((a, b) => a.name.localeCompare(b.name));
1004
+
1005
+ for (const entry of dirEntries) {
1006
+ if (entries.length >= maxEntries) {
1007
+ return;
1008
+ }
1009
+ if (entry.name.startsWith(".") && entry.name !== ".ai") {
1010
+ continue;
1011
+ }
1012
+ if (entry.isDirectory() && ignoredDetectionDirs.has(entry.name)) {
1013
+ continue;
1014
+ }
1015
+
1016
+ const absolutePath = path.join(currentRoot, entry.name);
1017
+ const relativePath = path.relative(root, absolutePath).replaceAll("\\", "/");
1018
+ entries.push({ relativePath, isDirectory: entry.isDirectory() });
1019
+
1020
+ if (entry.isDirectory()) {
1021
+ await walk(absolutePath, depth + 1);
1022
+ }
1023
+ }
1024
+ }
1025
+
1026
+ await walk(root, 0);
1027
+ return entries;
1028
+ }
1029
+
1030
+ async function collectProjectDiscovery(targetRoot) {
1031
+ const entries = await listDetectionEntries(targetRoot);
1032
+ const candidateTextFiles = [
1033
+ "README.md",
1034
+ "README.zh-CN.md",
1035
+ "readme.md",
1036
+ "package.json",
1037
+ "pyproject.toml",
1038
+ "requirements.txt",
1039
+ "Makefile",
1040
+ "Dockerfile",
1041
+ "docker-compose.yml"
1042
+ ];
1043
+ const textParts = [path.basename(targetRoot), targetRoot, ...entries.map((entry) => entry.relativePath)];
1044
+ let packageJson;
1045
+
1046
+ for (const fileName of candidateTextFiles) {
1047
+ const filePath = path.join(targetRoot, fileName);
1048
+ if (!(await exists(filePath))) {
1049
+ continue;
1050
+ }
1051
+ const content = await readTextIfExists(filePath);
1052
+ textParts.push(fileName, content.slice(0, 12000));
1053
+ if (fileName === "package.json") {
1054
+ try {
1055
+ packageJson = JSON.parse(content);
1056
+ } catch {
1057
+ packageJson = undefined;
1058
+ }
1059
+ }
1060
+ }
1061
+
1062
+ const directories = entries.filter((entry) => entry.isDirectory).map((entry) => entry.relativePath);
1063
+ const files = entries.filter((entry) => !entry.isDirectory).map((entry) => entry.relativePath);
1064
+ const projectName = packageJson?.name ?? path.basename(targetRoot);
1065
+
1066
+ return {
1067
+ targetRoot,
1068
+ projectName,
1069
+ packageJson,
1070
+ directories,
1071
+ files,
1072
+ signalText: textParts.join("\n")
1073
+ };
1074
+ }
1075
+
1076
+ function detectProjectContext(manifest, discovery) {
1077
+ const profileScores = scoreDetectionRules(profileDetectionRules, discovery.signalText);
1078
+ const skillScores = scoreDetectionRules(skillDetectionRules, discovery.signalText);
1079
+ const profileDescriptions = new Map((manifest.projectProfiles ?? []).map((profile) => [profile.id, profile.description]));
1080
+ const skillMetadata = new Map((manifest.skills ?? []).map((skill) => [skill.id, skill]));
1081
+
1082
+ return {
1083
+ profileScores: profileScores.map((item) => ({
1084
+ ...item,
1085
+ description: profileDescriptions.get(item.id) ?? item.id
1086
+ })),
1087
+ skillScores: skillScores.map((item) => ({
1088
+ ...item,
1089
+ metadata: skillMetadata.get(item.id)
1090
+ }))
1091
+ };
1092
+ }
1093
+
1094
+ function resolveProfileSelection(profileOption, manifest, detection) {
1095
+ const profiles = manifest.projectProfiles ?? [];
1096
+ const profileIds = new Set(profiles.map((profile) => profile.id));
1097
+ const option = profileOption ?? "auto";
1098
+
1099
+ if (option === "auto") {
1100
+ return {
1101
+ mode: "auto",
1102
+ profileIds: detection.profileScores
1103
+ .filter((item) => item.score >= 2 && profileIds.has(item.id))
1104
+ .map((item) => item.id)
1105
+ };
1106
+ }
1107
+
1108
+ if (option === "none") {
1109
+ return { mode: "none", profileIds: [] };
1110
+ }
1111
+
1112
+ if (option === "all") {
1113
+ return { mode: "all", profileIds: profiles.map((profile) => profile.id) };
1114
+ }
1115
+
1116
+ if (!profileIds.has(option)) {
1117
+ throw new Error(`Unsupported project profile: ${option}`);
1118
+ }
1119
+
1120
+ return { mode: "manual", profileIds: [option] };
1121
+ }
1122
+
1123
+ function renderMatchList(scores, emptyText) {
1124
+ if (scores.length === 0) {
1125
+ return [`- ${emptyText}`];
1126
+ }
1127
+
1128
+ return scores.map((item) => {
1129
+ const matches = item.matches.slice(0, 8).join("、");
1130
+ return `- \`${item.id}\`:${item.confidence},命中 ${item.score} 项(${matches})`;
1131
+ });
1132
+ }
1133
+
1134
+ function renderProfileDetectionDoc(manifest, discovery, detection, selection) {
1135
+ const profiles = manifest.projectProfiles ?? [];
1136
+ const selected = selection.profileIds.length > 0
1137
+ ? selection.profileIds.map((id) => `\`${id}\``).join("、")
1138
+ : "未自动启用 profile";
1139
+ const profileRows = profiles.map((profile) => `| \`${profile.id}\` | ${profile.description} |`);
1140
+
1141
+ return [
1142
+ "# Profile Detection",
1143
+ "",
1144
+ "> 本文件由 `archsight-aios init` 根据本地项目名、README、package / pyproject 和浅层文件名生成。它是 AIOS 自动识别草稿,不是业务事实或工程结论;如与项目实际不符,以 `.ai/project-context.md` 和人工说明为准。",
1145
+ "",
1146
+ "## 识别结论",
1147
+ "",
1148
+ `- 模式:\`${selection.mode}\``,
1149
+ `- 当前启用 profile:${selected}`,
1150
+ "- 规则来源:ArchSight AIOS 内置 profile registry;未复制到本项目的 profile 仍可作为包内模板被工具读取。",
1151
+ "",
1152
+ "## Profile 命中证据",
1153
+ "",
1154
+ ...renderMatchList(detection.profileScores, "未命中明确行业 profile;按通用项目接入,遇到具体资料时再由任务上下文触发对应 Skill。"),
1155
+ "",
1156
+ "## Skill 候选",
1157
+ "",
1158
+ ...renderMatchList(detection.skillScores, "未命中明确 Skill;按用户任务和项目上下文动态选择。"),
1159
+ "",
1160
+ "## 可用 Profile Registry",
1161
+ "",
1162
+ "| Profile | 适用范围 |",
1163
+ "| --- | --- |",
1164
+ ...profileRows,
1165
+ "",
1166
+ "## 边界",
1167
+ "",
1168
+ "- 自动识别只用于降低初始化门槛,不替代人工确认。",
1169
+ "- 不因为安装 AIOS 就默认把项目判定为 BIM、施工视觉或 RAG 项目。",
1170
+ "- 涉及金额、工期、法律责任、结构安全、规范合规或人员信息时,必须保留人工复核。",
1171
+ ""
1172
+ ].join("\n");
1173
+ }
1174
+
1175
+ function dependencyNames(packageJson) {
1176
+ return [
1177
+ ...Object.keys(packageJson?.dependencies ?? {}),
1178
+ ...Object.keys(packageJson?.devDependencies ?? {}),
1179
+ ...Object.keys(packageJson?.peerDependencies ?? {})
1180
+ ];
1181
+ }
1182
+
1183
+ function hasAnySignal(discovery, words) {
1184
+ const signal = normalizeSignal(discovery.signalText);
1185
+ return words.some((word) => signal.includes(normalizeSignal(word)));
1186
+ }
1187
+
1188
+ function inferStack(discovery) {
1189
+ const deps = dependencyNames(discovery.packageJson);
1190
+ const depSet = new Set(deps.map((dep) => dep.toLowerCase()));
1191
+ const hasDep = (...names) => names.some((name) => depSet.has(name));
1192
+ const hasFile = (...names) => names.some((name) => discovery.files.includes(name) || discovery.directories.includes(name));
1193
+
1194
+ const frontend = [];
1195
+ if (hasDep("react", "next", "vite", "vue", "@angular/core", "svelte")) {
1196
+ frontend.push(deps.filter((dep) => ["react", "next", "vite", "vue", "@angular/core", "svelte"].includes(dep)).join(" / "));
1197
+ }
1198
+ if (hasFile("src", "app", "pages")) {
1199
+ frontend.push("存在前端入口目录");
1200
+ }
1201
+
1202
+ const backend = [];
1203
+ if (hasDep("express", "fastify", "@nestjs/core", "koa", "hono")) {
1204
+ backend.push(deps.filter((dep) => ["express", "fastify", "@nestjs/core", "koa", "hono"].includes(dep)).join(" / "));
1205
+ }
1206
+ if (hasAnySignal(discovery, ["fastapi", "django", "flask", "spring boot"])) {
1207
+ backend.push("检测到后端框架关键词");
1208
+ }
1209
+
1210
+ const database = [];
1211
+ if (hasDep("prisma", "drizzle-orm", "typeorm", "sequelize", "mongoose", "pg", "mysql2")) {
1212
+ database.push(deps.filter((dep) => ["prisma", "drizzle-orm", "typeorm", "sequelize", "mongoose", "pg", "mysql2"].includes(dep)).join(" / "));
1213
+ }
1214
+ if (hasAnySignal(discovery, ["postgres", "mysql", "sqlite", "redis", "mongodb", "sqlalchemy"])) {
1215
+ database.push("检测到数据库关键词");
1216
+ }
1217
+
1218
+ const ai = [];
1219
+ if (hasDep("openai", "langchain", "@langchain/core", "llamaindex", "ai")) {
1220
+ ai.push(deps.filter((dep) => ["openai", "langchain", "@langchain/core", "llamaindex", "ai"].includes(dep)).join(" / "));
1221
+ }
1222
+ if (hasAnySignal(discovery, ["rag", "graphrag", "embedding", "mcp", "agent", "向量", "知识库"])) {
1223
+ ai.push("检测到 AI / RAG / Agent 关键词");
1224
+ }
1225
+
1226
+ const deployment = [];
1227
+ if (hasFile("Dockerfile", "docker-compose.yml", ".github")) {
1228
+ deployment.push("检测到 Docker / CI 文件");
1229
+ }
1230
+ if (hasAnySignal(discovery, ["vercel", "railway", "kubernetes", "k8s", "helm"])) {
1231
+ deployment.push("检测到部署平台关键词");
1232
+ }
1233
+
1234
+ return {
1235
+ frontend: frontend.filter(Boolean).join(";") || "待补充",
1236
+ backend: backend.filter(Boolean).join(";") || "待补充",
1237
+ database: database.filter(Boolean).join(";") || "待补充",
1238
+ ai: ai.filter(Boolean).join(";") || "待补充",
1239
+ deployment: deployment.filter(Boolean).join(";") || "待补充"
1240
+ };
1241
+ }
1242
+
1243
+ function scriptCommand(discovery, scriptName, fallback = "待补充") {
1244
+ if (discovery.packageJson?.scripts?.[scriptName]) {
1245
+ return `npm run ${scriptName}`;
1246
+ }
1247
+ return fallback;
1248
+ }
1249
+
1250
+ function renderProjectContextDoc(manifest, discovery, detection, selection) {
1251
+ const stack = inferStack(discovery);
1252
+ const description = discovery.packageJson?.description || "由 AIOS 根据项目入口自动生成的项目上下文草稿,请按实际业务补充目标、用户、阶段和边界。";
1253
+ const selectedProfiles = selection.profileIds.length > 0
1254
+ ? selection.profileIds.map((id) => `\`${id}\``).join("、")
1255
+ : "未自动启用 profile";
1256
+ const selectedSkills = detection.skillScores.length > 0
1257
+ ? detection.skillScores.slice(0, 8).map((item) => `\`${item.id}\``).join("、")
1258
+ : "按任务动态选择";
1259
+ const selectedSkillIds = new Set(detection.skillScores.map((item) => item.id));
1260
+ const agents = [...new Set((manifest.skills ?? [])
1261
+ .filter((skill) => selectedSkillIds.has(skill.id))
1262
+ .map((skill) => routeAgentName(manifest, skill.primaryAgent)))]
1263
+ .join("、") || "按任务动态选择";
1264
+ const workflows = [...new Set((manifest.skills ?? [])
1265
+ .filter((skill) => selectedSkillIds.has(skill.id))
1266
+ .map((skill) => skill.defaultWorkflow))]
1267
+ .map((workflow) => `\`${workflow}\``)
1268
+ .join("、") || "按任务动态选择";
1269
+ const codeStructure = [
1270
+ ...discovery.directories.slice(0, 12).map((item) => `- \`${item}/\``),
1271
+ ...discovery.files.filter((item) => ["package.json", "pyproject.toml", "README.md", "Makefile", "Dockerfile"].includes(item)).map((item) => `- \`${item}\``)
1272
+ ];
1273
+
1274
+ return [
1275
+ "# Project Context",
1276
+ "",
1277
+ "> 本文件由 `archsight-aios init` 首次创建并自动预填。请把它当作可编辑草稿;AIOS 不会在重复初始化时覆盖已有项目上下文。",
1278
+ "",
1279
+ "## 项目名称",
1280
+ "",
1281
+ discovery.projectName,
1282
+ "",
1283
+ "## 项目定位",
1284
+ "",
1285
+ description,
1286
+ "",
1287
+ "## 技术栈",
1288
+ "",
1289
+ `- 前端:${stack.frontend}`,
1290
+ `- 后端:${stack.backend}`,
1291
+ `- 数据库:${stack.database}`,
1292
+ `- AI / RAG / Agent:${stack.ai}`,
1293
+ `- 部署环境:${stack.deployment}`,
1294
+ "",
1295
+ "## 代码结构",
1296
+ "",
1297
+ ...(codeStructure.length > 0 ? codeStructure : ["- 待补充"]),
1298
+ "",
1299
+ "## 常用命令",
1300
+ "",
1301
+ "```text",
1302
+ `安装:${discovery.packageJson ? "npm install" : "待补充"}`,
1303
+ `开发:${scriptCommand(discovery, "dev")}`,
1304
+ `测试:${scriptCommand(discovery, "test")}`,
1305
+ `构建:${scriptCommand(discovery, "build")}`,
1306
+ `Lint:${scriptCommand(discovery, "lint")}`,
1307
+ `类型检查:${scriptCommand(discovery, "typecheck", scriptCommand(discovery, "type-check"))}`,
1308
+ "部署前检查:待补充",
1309
+ "```",
1310
+ "",
1311
+ "## 关键约束",
1312
+ "",
1313
+ "- 不得修改:项目已有入口文件中非 AIOS 托管块的内容,除非用户明确要求。",
1314
+ "- 必须保持:当前项目事实、测试、构建和发布流程优先于 AIOS 通用模板。",
1315
+ "- 需要人工确认:业务范围、客户资料、金额、工期、法律责任、规范合规、结构安全和人员信息。",
1316
+ "- 已知风险:自动识别只基于本地文件名和少量文本入口,可能漏判或误判;以人工修订后的项目上下文为准。",
1317
+ "",
1318
+ "## 当前接入的 ArchSight AIOS 能力",
1319
+ "",
1320
+ `- Profile:${selectedProfiles}`,
1321
+ `- Agent:${agents}`,
1322
+ `- Skills:${selectedSkills}`,
1323
+ `- Workflows:${workflows}`,
1324
+ "- Runtime:本地 `.ai/` 规则、AIOS Skill / Workflow、可选 Capability 工具证据。",
1325
+ ""
1326
+ ].join("\n");
1327
+ }
1328
+
848
1329
  async function resolveInitProjectMode(requestedMode, targetRoot, rootInstructionFiles, aiFiles) {
849
1330
  if (requestedMode !== "auto") {
850
1331
  return requestedMode;
@@ -899,7 +1380,16 @@ async function install(options) {
899
1380
  throw new Error("Only --scope user is supported in this release.");
900
1381
  }
901
1382
 
902
- const validTargets = new Set(["codex", "agents", "gemini", "antigravity", "workbuddy", "all"]);
1383
+ const targetAliases = {
1384
+ claudecode: "claude-code",
1385
+ claude_code: "claude-code",
1386
+ claude: "claude-code",
1387
+ "open-code": "opencode",
1388
+ open_code: "opencode"
1389
+ };
1390
+ options.target = targetAliases[options.target] ?? options.target;
1391
+
1392
+ const validTargets = new Set(["codex", "agents", "gemini", "antigravity", "workbuddy", "opencode", "claude-code", "all"]);
903
1393
  if (!validTargets.has(options.target)) {
904
1394
  throw new Error(`Unsupported target: ${options.target}`);
905
1395
  }
@@ -907,7 +1397,7 @@ async function install(options) {
907
1397
  const skillNames = await listAiosSkills();
908
1398
  const workflowPaths = await listAiosWorkflowPaths();
909
1399
  const targets = options.target === "all"
910
- ? ["codex", "gemini", "antigravity", "workbuddy"]
1400
+ ? ["codex", "gemini", "antigravity", "workbuddy", "opencode", "claude-code"]
911
1401
  : [options.target];
912
1402
 
913
1403
  const installed = [];
@@ -955,6 +1445,18 @@ async function install(options) {
955
1445
  installed.push("workbuddy skills");
956
1446
  }
957
1447
 
1448
+ if (targets.includes("opencode")) {
1449
+ await removeLegacySkillDirs(openCodeSkillsRoot(), skillNames);
1450
+ await installSkillsTo(openCodeSkillsRoot(), skillNames);
1451
+ installed.push("opencode skills");
1452
+ }
1453
+
1454
+ if (targets.includes("claude-code")) {
1455
+ await removeLegacySkillDirs(claudeCodeSkillsRoot(), skillNames);
1456
+ await installSkillsTo(claudeCodeSkillsRoot(), skillNames);
1457
+ installed.push("claude-code skills");
1458
+ }
1459
+
958
1460
  console.log(`Installed: ${installed.join(", ")}`);
959
1461
  console.log(`Skills: ${skillNames.join(", ")}`);
960
1462
  console.log(`Workflows: ${workflowPaths.map((workflowPath) => path.basename(workflowPath)).join(", ")}`);
@@ -1005,6 +1507,8 @@ async function doctor() {
1005
1507
  );
1006
1508
  checkCondition("codex workflows target", manifest.installTargets?.codexWorkflows === "~/.codex/workflows/aios", "codexWorkflows");
1007
1509
  checkCondition("workbuddy skills target", manifest.installTargets?.workBuddySkills === "~/.workbuddy/skills", "workBuddySkills");
1510
+ checkCondition("opencode skills target", manifest.installTargets?.openCodeSkills === "~/.opencode/skills", "openCodeSkills");
1511
+ checkCondition("claude-code skills target", manifest.installTargets?.claudeCodeSkills === "~/.claude/skills", "claudeCodeSkills");
1008
1512
  checkCondition("antigravity plugin target", manifest.installTargets?.antigravityPlugin === "~/.gemini/config/plugins/archsight-aios", "antigravityPlugin");
1009
1513
  checkCondition("antigravity legacy skills target", manifest.installTargets?.antigravityLegacySkills === "~/.gemini/antigravity/skills", "antigravityLegacySkills");
1010
1514
 
@@ -1107,6 +1611,8 @@ async function doctor() {
1107
1611
  await check("gemini support delivery", path.join(geminiRoot, "delivery"));
1108
1612
  await check("gemini support memory", path.join(geminiRoot, "memory"));
1109
1613
  await check("codex skills root", path.join(home, ".codex", "skills"));
1614
+ await check("opencode skills root", openCodeSkillsRoot());
1615
+ await check("claude-code skills root", claudeCodeSkillsRoot());
1110
1616
  await check("codex workflows root", path.join(home, ".codex", "workflows", "aios"));
1111
1617
  await check("gemini instructions", path.join(home, ".gemini", "GEMINI.md"));
1112
1618
  await checkContains("gemini managed block", path.join(home, ".gemini", "GEMINI.md"), managedStart);
@@ -1127,6 +1633,8 @@ async function doctor() {
1127
1633
  await check(`gemini support skill ${skillName}`, path.join(geminiRoot, sourceDir, "SKILL.md"));
1128
1634
  await check(`codex skill ${skillName}`, path.join(home, ".codex", "skills", skillName, "SKILL.md"));
1129
1635
  await check(`workbuddy skill ${skillName}`, path.join(workBuddySkillsRoot(), skillName, "SKILL.md"));
1636
+ await check(`opencode skill ${skillName}`, path.join(openCodeSkillsRoot(), skillName, "SKILL.md"));
1637
+ await check(`claude-code skill ${skillName}`, path.join(claudeCodeSkillsRoot(), skillName, "SKILL.md"));
1130
1638
  if (useAntigravityLegacy) {
1131
1639
  await check(`antigravity 1.x legacy skill ${skillName}`, path.join(antigravityLegacySkillsRoot(), skillName, "SKILL.md"));
1132
1640
  }
@@ -1162,19 +1670,22 @@ async function initProject(options) {
1162
1670
  "AGENTS.md",
1163
1671
  "AI_CODING_RULES.md",
1164
1672
  "CLAUDE.md",
1165
- "GEMINI.md"
1673
+ "GEMINI.md",
1674
+ "OPENCODE.md"
1166
1675
  ];
1167
1676
  const linkedInstructionFiles = [
1168
1677
  "AGENTS.md",
1169
1678
  "CLAUDE.md",
1170
- "GEMINI.md"
1679
+ "GEMINI.md",
1680
+ "OPENCODE.md"
1171
1681
  ];
1172
1682
  const aiFiles = [
1173
1683
  path.join(".ai", "ARCHSIGHT_AIOS_RULES.md"),
1174
1684
  path.join(".ai", "project-context.md"),
1175
1685
  path.join(".ai", "agent-routing.md"),
1176
1686
  path.join(".ai", "skills.md"),
1177
- path.join(".ai", "workflows.md")
1687
+ path.join(".ai", "workflows.md"),
1688
+ path.join(".ai", "profile-detection.md")
1178
1689
  ];
1179
1690
  const mode = await resolveInitProjectMode(options.mode ?? "auto", targetRoot, rootInstructionFiles, aiFiles);
1180
1691
  const files = mode === "ai-only"
@@ -1190,6 +1701,9 @@ async function initProject(options) {
1190
1701
  }
1191
1702
 
1192
1703
  await ensureDir(targetRoot);
1704
+ const discovery = await collectProjectDiscovery(targetRoot);
1705
+ const detection = detectProjectContext(manifest, discovery);
1706
+ const profileSelection = resolveProfileSelection(options.profile, manifest, detection);
1193
1707
 
1194
1708
  for (const fileName of files) {
1195
1709
  const src = path.join(templateRoot, fileName);
@@ -1200,7 +1714,13 @@ async function initProject(options) {
1200
1714
  continue;
1201
1715
  }
1202
1716
  await ensureDir(path.dirname(dest));
1203
- await fs.copyFile(src, dest);
1717
+ if (fileName === path.join(".ai", "project-context.md")) {
1718
+ await fs.writeFile(dest, renderProjectContextDoc(manifest, discovery, detection, profileSelection), "utf8");
1719
+ } else if (fileName === path.join(".ai", "profile-detection.md")) {
1720
+ await fs.writeFile(dest, renderProfileDetectionDoc(manifest, discovery, detection, profileSelection), "utf8");
1721
+ } else {
1722
+ await fs.copyFile(src, dest);
1723
+ }
1204
1724
  console.log(`CREATE ${dest}`);
1205
1725
  }
1206
1726
 
@@ -1221,11 +1741,14 @@ async function initProject(options) {
1221
1741
  }
1222
1742
  }
1223
1743
 
1224
- if (options.profile) {
1225
- const profile = manifest.projectProfiles?.find((item) => item.id === options.profile);
1226
- if (!profile) {
1227
- throw new Error(`Unsupported project profile: ${options.profile}`);
1228
- }
1744
+ if (profileSelection.profileIds.length > 0) {
1745
+ console.log(`PROFILE ${profileSelection.mode}: ${profileSelection.profileIds.join(", ")}`);
1746
+ } else {
1747
+ console.log(`PROFILE ${profileSelection.mode}: none`);
1748
+ }
1749
+
1750
+ for (const profileId of profileSelection.profileIds) {
1751
+ const profile = manifest.projectProfiles?.find((item) => item.id === profileId);
1229
1752
  await copyTreeMissing(path.join(repoRoot, profile.path), targetRoot);
1230
1753
  }
1231
1754
  }
@@ -1281,7 +1804,9 @@ async function validateProjectTemplate(options) {
1281
1804
  ["project AIOS rules uses AIOS", path.join(".ai", "ARCHSIGHT_AIOS_RULES.md")],
1282
1805
  ["project CLAUDE uses AIOS", "CLAUDE.md"],
1283
1806
  ["project GEMINI uses AIOS", "GEMINI.md"],
1284
- ["project context uses AIOS", path.join(".ai", "project-context.md")]
1807
+ ["project OPENCODE uses AIOS", "OPENCODE.md"],
1808
+ ["project context uses AIOS", path.join(".ai", "project-context.md")],
1809
+ ["project profile detection uses AIOS", path.join(".ai", "profile-detection.md")]
1285
1810
  ]) {
1286
1811
  const content = await fs.readFile(path.join(targetRoot, fileName), "utf8");
1287
1812
  const legacyAiOsText = ["AI", "OS"].join(" ");
@@ -1292,13 +1817,19 @@ async function validateProjectTemplate(options) {
1292
1817
  });
1293
1818
  }
1294
1819
 
1295
- if (options.profile) {
1296
- const profile = manifest.projectProfiles?.find((item) => item.id === options.profile);
1820
+ const profileCheckIds = options.profile === "all"
1821
+ ? (manifest.projectProfiles ?? []).map((profile) => profile.id)
1822
+ : options.profile && !["auto", "none"].includes(options.profile)
1823
+ ? [options.profile]
1824
+ : [];
1825
+
1826
+ for (const profileId of profileCheckIds) {
1827
+ const profile = manifest.projectProfiles?.find((item) => item.id === profileId);
1297
1828
  if (!profile) {
1298
- throw new Error(`Unsupported project profile: ${options.profile}`);
1829
+ throw new Error(`Unsupported project profile: ${profileId}`);
1299
1830
  }
1300
1831
  for (const fileName of profile.requiredFiles ?? []) {
1301
- await check(`profile output ${options.profile}/${fileName}`, path.join(targetRoot, fileName));
1832
+ await check(`profile output ${profileId}/${fileName}`, path.join(targetRoot, fileName));
1302
1833
  }
1303
1834
  }
1304
1835