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