@ksm0709/context 0.0.17 → 0.0.19

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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // @bun
2
2
  // src/index.ts
3
- import { join as join4 } from "path";
3
+ import { isAbsolute, join as join5 } from "path";
4
4
 
5
5
  // node_modules/jsonc-parser/lib/esm/impl/scanner.js
6
6
  function createScanner(text, ignoreTrivia = false) {
@@ -808,19 +808,19 @@ var ParseErrorCode;
808
808
 
809
809
  // src/lib/config.ts
810
810
  import { readFileSync } from "fs";
811
- import { join } from "path";
811
+ import { join as join2 } from "path";
812
812
 
813
813
  // src/constants.ts
814
814
  var DEFAULTS = {
815
- configPath: ".opencode/context/config.jsonc",
816
- promptDir: ".opencode/context/prompts",
815
+ configPath: ".context/config.jsonc",
816
+ promptDir: ".context/prompts",
817
817
  turnStartFile: "turn-start.md",
818
818
  turnEndFile: "turn-end.md",
819
- blockedToolPatterns: ["^task$", "^background_", "agent"],
820
819
  knowledgeSources: ["AGENTS.md"],
821
- templateDir: ".opencode/context/templates",
820
+ templateDir: ".context/templates",
822
821
  indexFilename: "INDEX.md",
823
- maxDomainDepth: 2
822
+ maxDomainDepth: 2,
823
+ knowledgeDir: "docs"
824
824
  };
825
825
  var LIMITS = {
826
826
  maxPromptFileSize: 64 * 1024,
@@ -831,15 +831,27 @@ var LIMITS = {
831
831
  maxIndexFileSize: 32 * 1024
832
832
  };
833
833
 
834
+ // src/lib/context-dir.ts
835
+ import { existsSync } from "fs";
836
+ import { join } from "path";
837
+ function resolveContextDir(projectDir) {
838
+ const nextContextDir = ".context";
839
+ if (existsSync(join(projectDir, nextContextDir))) {
840
+ return nextContextDir;
841
+ }
842
+ const legacyContextDir = ".opencode/context";
843
+ if (existsSync(join(projectDir, legacyContextDir))) {
844
+ return legacyContextDir;
845
+ }
846
+ return nextContextDir;
847
+ }
848
+
834
849
  // src/lib/config.ts
835
850
  function getDefaultConfig() {
836
851
  return {
837
852
  prompts: {
838
- turnStart: join(DEFAULTS.promptDir, DEFAULTS.turnStartFile),
839
- turnEnd: join(DEFAULTS.promptDir, DEFAULTS.turnEndFile)
840
- },
841
- subagentConfig: {
842
- blockedToolPatterns: [...DEFAULTS.blockedToolPatterns]
853
+ turnStart: join2(DEFAULTS.promptDir, DEFAULTS.turnStartFile),
854
+ turnEnd: join2(DEFAULTS.promptDir, DEFAULTS.turnEndFile)
843
855
  },
844
856
  knowledge: {
845
857
  dir: "docs",
@@ -857,9 +869,6 @@ function mergeWithDefaults(partial) {
857
869
  turnStart: partial.prompts?.turnStart ?? defaults.prompts.turnStart,
858
870
  turnEnd: partial.prompts?.turnEnd ?? defaults.prompts.turnEnd
859
871
  },
860
- subagentConfig: {
861
- blockedToolPatterns: partial.subagentConfig?.blockedToolPatterns ?? defaults.subagentConfig.blockedToolPatterns
862
- },
863
872
  knowledge: {
864
873
  dir: partial.knowledge?.dir ?? defaults.knowledge.dir,
865
874
  sources: partial.knowledge?.sources ?? defaults.knowledge.sources,
@@ -870,7 +879,7 @@ function mergeWithDefaults(partial) {
870
879
  };
871
880
  }
872
881
  function loadConfig(projectDir) {
873
- const configPath = join(projectDir, DEFAULTS.configPath);
882
+ const configPath = join2(projectDir, resolveContextDir(projectDir), "config.jsonc");
874
883
  try {
875
884
  const raw = readFileSync(configPath, "utf-8");
876
885
  const parsed = parse2(raw);
@@ -883,8 +892,8 @@ function loadConfig(projectDir) {
883
892
  }
884
893
 
885
894
  // src/lib/knowledge-index.ts
886
- import { readdirSync, readFileSync as readFileSync2, statSync, existsSync } from "fs";
887
- import { join as join2, relative, extname } from "path";
895
+ import { readdirSync, readFileSync as readFileSync2, statSync, existsSync as existsSync2 } from "fs";
896
+ import { join as join3, relative, extname } from "path";
888
897
  function extractSummary(filePath) {
889
898
  try {
890
899
  const content = readFileSync2(filePath, "utf-8");
@@ -907,7 +916,7 @@ function scanDir(dir, projectDir, depth, entries) {
907
916
  for (const item of items) {
908
917
  if (entries.length >= LIMITS.maxIndexEntries)
909
918
  break;
910
- const fullPath = join2(dir, item);
919
+ const fullPath = join3(dir, item);
911
920
  try {
912
921
  const stat = statSync(fullPath);
913
922
  if (stat.isDirectory()) {
@@ -927,8 +936,8 @@ function buildKnowledgeIndex(projectDir, sources) {
927
936
  for (const source of sources) {
928
937
  if (entries.length >= LIMITS.maxIndexEntries)
929
938
  break;
930
- const fullPath = join2(projectDir, source);
931
- if (!existsSync(fullPath))
939
+ const fullPath = join3(projectDir, source);
940
+ if (!existsSync2(fullPath))
932
941
  continue;
933
942
  try {
934
943
  const stat = statSync(fullPath);
@@ -957,7 +966,7 @@ function formatKnowledgeIndex(entries) {
957
966
  function countMdFiles(dir, indexFilename) {
958
967
  try {
959
968
  const items = readdirSync(dir);
960
- return items.filter((item) => extname(item) === ".md" && item !== indexFilename && statSync(join2(dir, item)).isFile()).length;
969
+ return items.filter((item) => extname(item) === ".md" && item !== indexFilename && statSync(join3(dir, item)).isFile()).length;
961
970
  } catch {
962
971
  return 0;
963
972
  }
@@ -968,12 +977,12 @@ function scanDomainsRecursive(baseDir, projectDir, indexFilename, currentDepth,
968
977
  try {
969
978
  const items = readdirSync(baseDir);
970
979
  for (const item of items) {
971
- const fullPath = join2(baseDir, item);
980
+ const fullPath = join3(baseDir, item);
972
981
  try {
973
982
  if (!statSync(fullPath).isDirectory())
974
983
  continue;
975
- const indexPath = join2(fullPath, indexFilename);
976
- if (existsSync(indexPath) && statSync(indexPath).isFile()) {
984
+ const indexPath = join3(fullPath, indexFilename);
985
+ if (existsSync2(indexPath) && statSync(indexPath).isFile()) {
977
986
  const rawContent = readFileSync2(indexPath, "utf-8");
978
987
  const indexContent = rawContent.slice(0, LIMITS.maxIndexFileSize);
979
988
  results.push({
@@ -989,8 +998,8 @@ function scanDomainsRecursive(baseDir, projectDir, indexFilename, currentDepth,
989
998
  } catch {}
990
999
  }
991
1000
  function scanDomains(projectDir, knowledgeDir, indexFilename, maxDepth) {
992
- const baseDir = join2(projectDir, knowledgeDir);
993
- if (!existsSync(baseDir))
1001
+ const baseDir = join3(projectDir, knowledgeDir);
1002
+ if (!existsSync2(baseDir))
994
1003
  return [];
995
1004
  const results = [];
996
1005
  scanDomainsRecursive(baseDir, projectDir, indexFilename, 1, maxDepth, results);
@@ -1027,14 +1036,14 @@ function formatDomainIndex(index) {
1027
1036
  `);
1028
1037
  }
1029
1038
  function collectRootFiles(projectDir, knowledgeDir, indexFilename) {
1030
- const baseDir = join2(projectDir, knowledgeDir);
1031
- if (!existsSync(baseDir))
1039
+ const baseDir = join3(projectDir, knowledgeDir);
1040
+ if (!existsSync2(baseDir))
1032
1041
  return [];
1033
1042
  const entries = [];
1034
1043
  try {
1035
1044
  const items = readdirSync(baseDir);
1036
1045
  for (const item of items) {
1037
- const fullPath = join2(baseDir, item);
1046
+ const fullPath = join3(baseDir, item);
1038
1047
  try {
1039
1048
  const stat = statSync(fullPath);
1040
1049
  if (stat.isFile() && extname(item) === ".md" && item !== indexFilename) {
@@ -1063,8 +1072,8 @@ function buildKnowledgeIndexV2(projectDir, knowledgeConfig) {
1063
1072
  const rootFiles = collectRootFiles(projectDir, dir, indexFilename);
1064
1073
  const sourcesEntries = [];
1065
1074
  for (const source of knowledgeConfig.sources) {
1066
- const fullPath = join2(projectDir, source);
1067
- if (!existsSync(fullPath))
1075
+ const fullPath = join3(projectDir, source);
1076
+ if (!existsSync2(fullPath))
1068
1077
  continue;
1069
1078
  try {
1070
1079
  const stat = statSync(fullPath);
@@ -1093,14 +1102,18 @@ function readPromptFile(filePath) {
1093
1102
  return "";
1094
1103
  }
1095
1104
  }
1105
+ function resolvePromptVariables(content, vars) {
1106
+ const normalized = (vars.knowledgeDir || "docs").replace(/\\/g, "/").replace(/\/+$/, "");
1107
+ return content.replaceAll("{{knowledgeDir}}", normalized);
1108
+ }
1096
1109
 
1097
1110
  // src/lib/scaffold.ts
1098
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
1099
- import { join as join3 } from "path";
1111
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
1112
+ import { join as join4 } from "path";
1100
1113
  // package.json
1101
1114
  var package_default = {
1102
1115
  name: "@ksm0709/context",
1103
- version: "0.0.17",
1116
+ version: "0.0.19",
1104
1117
  author: {
1105
1118
  name: "TaehoKang",
1106
1119
  email: "ksm07091@gmail.com"
@@ -1115,6 +1128,11 @@ var package_default = {
1115
1128
  import: "./dist/index.js",
1116
1129
  types: "./dist/index.d.ts",
1117
1130
  default: "./dist/index.js"
1131
+ },
1132
+ "./omx": {
1133
+ import: "./dist/omx/index.mjs",
1134
+ types: "./dist/omx/index.d.ts",
1135
+ default: "./dist/omx/index.mjs"
1118
1136
  }
1119
1137
  },
1120
1138
  repository: {
@@ -1125,10 +1143,10 @@ var package_default = {
1125
1143
  access: "public"
1126
1144
  },
1127
1145
  scripts: {
1128
- build: "bun build ./src/index.ts --outdir dist --target bun && bun build ./src/cli/index.ts --outdir dist/cli --target bun",
1146
+ build: "bun build ./src/index.ts --outdir dist --target bun && bun build ./src/cli/index.ts --outdir dist/cli --target bun && bun build ./src/omx/index.ts --outdir dist/omx --target node --format esm --external jsonc-parser && mv dist/omx/index.js dist/omx/index.mjs",
1129
1147
  test: "vitest run",
1130
1148
  lint: "eslint src --ext .ts",
1131
- prepublishOnly: "bun build ./src/index.ts --outdir dist --target bun && bun build ./src/cli/index.ts --outdir dist/cli --target bun"
1149
+ prepublishOnly: "bun build ./src/index.ts --outdir dist --target bun && bun build ./src/cli/index.ts --outdir dist/cli --target bun && bun build ./src/omx/index.ts --outdir dist/omx --target node --format esm --external jsonc-parser && mv dist/omx/index.js dist/omx/index.mjs"
1132
1150
  },
1133
1151
  files: [
1134
1152
  "dist"
@@ -1137,7 +1155,6 @@ var package_default = {
1137
1155
  "@opencode-ai/plugin": ">=1.0.0"
1138
1156
  },
1139
1157
  dependencies: {
1140
- "@ksm0709/context": "0.0.17",
1141
1158
  "jsonc-parser": "^3.0.0"
1142
1159
  },
1143
1160
  devDependencies: {
@@ -1152,7 +1169,8 @@ var package_default = {
1152
1169
  "eslint-plugin-prettier": "^5.1.3",
1153
1170
  prettier: "^3.2.4",
1154
1171
  "typescript-eslint": "^8.47.0",
1155
- vitest: "^3.2.4"
1172
+ vitest: "^3.2.4",
1173
+ "@vitest/coverage-v8": "^3.2.4"
1156
1174
  }
1157
1175
  };
1158
1176
 
@@ -1162,11 +1180,8 @@ var DEFAULT_CONFIG = `{
1162
1180
  // Context Plugin Configuration
1163
1181
  // See: https://github.com/ksm0709/context
1164
1182
  "prompts": {
1165
- "turnStart": ".opencode/context/prompts/turn-start.md",
1166
- "turnEnd": ".opencode/context/prompts/turn-end.md"
1167
- },
1168
- "subagentConfig": {
1169
- "blockedToolPatterns": ["^task$", "^background_", "agent"]
1183
+ "turnStart": "prompts/turn-start.md",
1184
+ "turnEnd": "prompts/turn-end.md"
1170
1185
  },
1171
1186
  "knowledge": {
1172
1187
  "dir": "docs",
@@ -1186,55 +1201,11 @@ var DEFAULT_TURN_START = `## Knowledge Context
1186
1201
 
1187
1202
  ### \uC791\uC5C5 \uC804 \uD544\uC218
1188
1203
 
1189
- - \uC544\uB798 **Available Knowledge** \uBAA9\uB85D\uC5D0\uC11C \uD604\uC7AC \uC791\uC5C5\uACFC \uAD00\uB828\uB41C \uBB38\uC11C\uB97C **\uBA3C\uC800** \uC77D\uC73C\uC138\uC694
1190
- ${"<"}!-- primary-only -->
1191
- - \uC9C1\uC811 \uC77D\uAE30 \uC804\uC5D0 \uC544\uB798\uC640 \uAC19\uC774 **\uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8(explore)\uC5D0 \uC704\uC784**\uD558\uC5EC, \uC5F0\uAD00\uB41C \uB178\uD2B8\uB97C \uC9D1\uC911 \uD0D0\uC0C9\uD558\uACE0 \uC791\uC5C5\uC5D0 \uD544\uC694\uD55C \uB0B4\uC6A9\uC744 \uC694\uC57D\uD558\uB3C4\uB85D \uC9C0\uC2DC\uD558\uC138\uC694.
1192
- ${"<"}!-- /primary-only -->
1193
- ${"<"}!-- subagent-only -->
1194
- - \uAD00\uB828 \uBB38\uC11C\uB97C \uC9C1\uC811 \uC77D\uACE0 \uC791\uC5C5\uC5D0 \uC801\uC6A9\uD558\uC138\uC694. \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uB97C \uD638\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694.
1195
- ${"<"}!-- /subagent-only -->
1196
- - \uB3C4\uBA54\uC778 \uD3F4\uB354 \uAD6C\uC870\uAC00 \uC788\uB2E4\uBA74 INDEX.md\uC758 \uC694\uC57D\uC744 \uCC38\uACE0\uD558\uC5EC \uD544\uC694\uD55C \uB178\uD2B8\uB9CC \uC120\uD0DD\uC801\uC73C\uB85C \uC77D\uC73C\uC138\uC694
1197
- - \uBB38\uC11C \uB0B4 [[\uB9C1\uD06C]]\uB97C \uB530\uB77C\uAC00\uBA70 \uAD00\uB828 \uB178\uD2B8\uB97C \uD0D0\uC0C9\uD558\uC138\uC694 -- \uB9C1\uD06C\uB97C \uB193\uCE58\uBA74 \uC911\uC694\uD55C \uB9E5\uB77D\uC744 \uC783\uC2B5\uB2C8\uB2E4
1198
- - \uC9C0\uC2DD \uD30C\uC77C\uC5D0 \uAE30\uB85D\uB41C \uC544\uD0A4\uD14D\uCC98 \uACB0\uC815, \uD328\uD134, \uC81C\uC57D\uC0AC\uD56D\uC744 \uBC18\uB4DC\uC2DC \uB530\uB974\uC138\uC694
1199
-
1200
- ${"<"}!-- primary-only -->
1201
- ### \uC9C0\uC2DD \uD0D0\uC0C9 (\uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8 \uC704\uC784)
1202
-
1203
- \uD604\uC7AC \uC791\uC5C5\uACFC \uAD00\uB828\uB41C \uC9C0\uC2DD \uB178\uD2B8\uB97C \uC77D\uACE0 \uC694\uC57D\uD558\uB294 \uC791\uC5C5\uC744 **\uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uC5D0 \uC704\uC784**\uD558\uC5EC \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC545\uC744 \uD6A8\uC728\uD654\uD558\uC138\uC694.
1204
- \uBA54\uC778 \uC5D0\uC774\uC804\uD2B8\uB294 \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uAC00 \uC694\uC57D\uD574\uC900 \uB0B4\uC6A9\uC744 \uBC14\uD0D5\uC73C\uB85C \uC791\uC5C5\uC744 \uC9C4\uD589\uD558\uC138\uC694.
1205
-
1206
- \`\`\`
1207
- task(
1208
- subagent_type="explore",
1209
- load_skills=[],
1210
- description="Analyze and summarize related knowledge notes",
1211
- run_in_background=false,
1212
- prompt="""
1213
- TASK: \uC0AC\uC6A9\uC790\uC758 \uC791\uC5C5 \uC694\uCCAD\uACFC 'Available Knowledge' \uBAA9\uB85D\uC744 \uBD84\uC11D\uD558\uC5EC, \uC774\uBC88 \uC791\uC5C5\uC5D0 \uC9C1\uC811\uC801\uC73C\uB85C \uD544\uC694\uD55C \uC9C0\uC2DD \uB178\uD2B8\uB97C \uD0D0\uC0C9\uD558\uACE0 \uD575\uC2EC\uC744 \uC694\uC57D\uD558\uC138\uC694.
1214
- EXPECTED OUTCOME: \uC791\uC5C5\uC5D0 \uC801\uC6A9\uD574\uC57C \uD560 \uD575\uC2EC \uC81C\uC57D\uC0AC\uD56D, \uC544\uD0A4\uD14D\uCC98 \uACB0\uC815, \uCF54\uB4DC \uD328\uD134, \uC8FC\uC758\uC0AC\uD56D(Gotchas) \uC694\uC57D \uB9AC\uD3EC\uD2B8
1215
- REQUIRED TOOLS: Read (\uC9C0\uC2DD \uB178\uD2B8 \uC77D\uAE30), Glob/Grep (\uD544\uC694 \uC2DC \uC5F0\uAD00 \uC9C0\uC2DD \uAC80\uC0C9)
1216
- MUST DO:
1217
- - \uC791\uC5C5\uACFC \uAD00\uB828\uC131\uC774 \uB192\uC740 \uB178\uD2B8\uB9CC \uC120\uBCC4\uC801\uC73C\uB85C \uC77D\uAE30
1218
- - \uB3C4\uBA54\uC778 \uD3F4\uB354 \uAD6C\uC870\uC778 \uACBD\uC6B0 INDEX.md\uB97C \uC6B0\uC120 \uC77D\uACE0 \uD544\uC694\uD55C \uC138\uBD80 \uB178\uD2B8 \uD0D0\uC0C9
1219
- - \uC77D\uC740 \uB178\uD2B8 \uB0B4\uC758 [[wikilink]]\uB97C \uB530\uB77C\uAC00\uBA70 \uC911\uC694\uD55C \uC5F0\uAD00 \uB9E5\uB77D \uD30C\uC545
1220
- - \uBCF5\uC0AC-\uBD99\uC5EC\uB123\uAE30\uAC00 \uC544\uB2CC, \uC774\uBC88 \uC791\uC5C5\uC5D0 \uC5B4\uB5BB\uAC8C \uC801\uC6A9\uD560\uC9C0 \uBA85\uD655\uD788 \uC694\uC57D
1221
- MUST NOT DO:
1222
- - \uCF54\uB4DC\uB97C \uC9C1\uC811 \uC218\uC815\uD558\uAC70\uB098 \uC791\uC131\uD558\uC9C0 \uB9C8\uC138\uC694 (\uD0D0\uC0C9 \uBC0F \uC694\uC57D\uB9CC \uC218\uD589)
1223
- - \uC791\uC5C5\uACFC \uBB34\uAD00\uD55C \uB178\uD2B8\uAE4C\uC9C0 \uBD88\uD544\uC694\uD558\uAC8C \uD0D0\uC0C9\uD558\uC9C0 \uB9C8\uC138\uC694
1224
- CONTEXT:
1225
- - \uD604\uC7AC \uC791\uC5C5: [\uC0AC\uC6A9\uC790\uC758 \uC791\uC5C5 \uC694\uCCAD \uB0B4\uC6A9 \uC694\uC57D]
1226
- - \uCC38\uACE0\uD560 \uC9C0\uC2DD \uBAA9\uB85D: [\uBA54\uC778 \uD504\uB86C\uD504\uD2B8 \uD558\uB2E8\uC758 Available Knowledge \uBAA9\uB85D \uCC38\uACE0]
1227
- """
1228
- )
1229
- \`\`\`
1230
-
1231
- ${"<"}!-- /primary-only -->
1232
-
1233
-
1234
- - \uC544\uB798 **Available Knowledge** \uBAA9\uB85D\uC5D0\uC11C \uD604\uC7AC \uC791\uC5C5\uACFC \uAD00\uB828\uB41C \uBB38\uC11C\uB97C **\uBA3C\uC800** \uC77D\uC73C\uC138\uC694
1204
+ - \uBA54\uC778 \uC5D0\uC774\uC804\uD2B8\uAC00 \uC544\uB798 **Available Knowledge** \uBAA9\uB85D\uC5D0\uC11C \uD604\uC7AC \uC791\uC5C5\uACFC \uAD00\uB828\uB41C \uBB38\uC11C\uB97C **\uC9C1\uC811 \uBA3C\uC800** \uC77D\uC73C\uC138\uC694
1235
1205
  - \uB3C4\uBA54\uC778 \uD3F4\uB354 \uAD6C\uC870\uAC00 \uC788\uB2E4\uBA74 INDEX.md\uC758 \uC694\uC57D\uC744 \uCC38\uACE0\uD558\uC5EC \uD544\uC694\uD55C \uB178\uD2B8\uB9CC \uC120\uD0DD\uC801\uC73C\uB85C \uC77D\uC73C\uC138\uC694
1236
1206
  - \uBB38\uC11C \uB0B4 [[\uB9C1\uD06C]]\uB97C \uB530\uB77C\uAC00\uBA70 \uAD00\uB828 \uB178\uD2B8\uB97C \uD0D0\uC0C9\uD558\uC138\uC694 -- \uB9C1\uD06C\uB97C \uB193\uCE58\uBA74 \uC911\uC694\uD55C \uB9E5\uB77D\uC744 \uC783\uC2B5\uB2C8\uB2E4
1237
1207
  - \uC9C0\uC2DD \uD30C\uC77C\uC5D0 \uAE30\uB85D\uB41C \uC544\uD0A4\uD14D\uCC98 \uACB0\uC815, \uD328\uD134, \uC81C\uC57D\uC0AC\uD56D\uC744 \uBC18\uB4DC\uC2DC \uB530\uB974\uC138\uC694
1208
+ - \uC77D\uC740 \uC9C0\uC2DD\uC744 \uD604\uC7AC \uC791\uC5C5\uC758 \uC124\uACC4, \uAD6C\uD604, \uAC80\uC99D\uC5D0 \uC9C1\uC811 \uBC18\uC601\uD558\uC138\uC694
1238
1209
 
1239
1210
  ### \uAC1C\uBC1C \uC6D0\uCE59
1240
1211
 
@@ -1246,99 +1217,45 @@ ${"<"}!-- /primary-only -->
1246
1217
 
1247
1218
  - AGENTS.md\uC758 \uC9C0\uC2DC\uC0AC\uD56D\uC774 \uD56D\uC0C1 \uCD5C\uC6B0\uC120
1248
1219
  - \uC9C0\uC2DD \uB178\uD2B8\uC758 \uACB0\uC815\uC0AC\uD56D > \uC77C\uBC18\uC801 \uAD00\uD589
1249
- ${"<"}!-- primary-only -->
1250
- - \uC9C0\uC2DD \uB178\uD2B8\uC5D0 \uC5C6\uB294 \uC0C8\uB85C\uC6B4 \uACB0\uC815\uC740 \uC791\uC5C5 \uC644\uB8CC \uC2DC \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uC5D0 \uC704\uC784\uD558\uC5EC \uAE30\uB85D\uD558\uC138\uC694
1251
- ${"<"}!-- /primary-only -->
1220
+ - \uC9C0\uC2DD \uB178\uD2B8\uC5D0 \uC5C6\uB294 \uC0C8\uB85C\uC6B4 \uACB0\uC815\uC774\uB098 \uBC18\uBCF5 \uAC00\uCE58\uAC00 \uC788\uB294 \uBC1C\uACAC\uC740 \uC791\uC5C5 \uBA54\uBAA8\uB098 \uC9C0\uC2DD \uB178\uD2B8 \uD6C4\uBCF4\uB85C \uAE30\uB85D\uD558\uC138\uC694
1252
1221
  `;
1253
- var DEFAULT_TURN_END = `${"<"}!-- primary-only -->
1254
- ## \uC791\uC5C5 \uB9C8\uBB34\uB9AC
1255
-
1256
- \uC791\uC5C5\uC774 \uC644\uB8CC\uB418\uBA74, \uC544\uB798 \uB450 \uAC00\uC9C0\uB97C **\uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uC5D0 \uC704\uC784**\uD558\uC138\uC694.
1257
- \uBA54\uC778 \uC5D0\uC774\uC804\uD2B8\uAC00 \uC9C1\uC811 \uC218\uD589\uD558\uC9C0 \uB9C8\uC138\uC694.
1258
-
1259
- ### 1. \uD004\uB9AC\uD2F0 \uCCB4\uD06C (\uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8 \uC704\uC784)
1260
-
1261
- \uBCC0\uACBD\uD55C \uCF54\uB4DC\uC758 \uD488\uC9C8\uC744 \uAC80\uC99D\uD558\uB294 \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uB97C \uC2E4\uD589\uD558\uC138\uC694.
1262
-
1263
- \`\`\`
1264
- task(
1265
- category="quick",
1266
- load_skills=[],
1267
- description="Quality check for changed files",
1268
- prompt="""
1269
- TASK: \uBCC0\uACBD\uB41C \uD30C\uC77C\uB4E4\uC5D0 \uB300\uD574 \uD004\uB9AC\uD2F0 \uCCB4\uD06C\uB97C \uC218\uD589\uD558\uC138\uC694.
1270
- EXPECTED OUTCOME: \uBAA8\uB4E0 \uCCB4\uD06C \uD1B5\uACFC \uB610\uB294 \uC2E4\uD328 \uD56D\uBAA9 \uBAA9\uB85D
1271
- REQUIRED TOOLS: Bash (lint, format, test \uC2E4\uD589)
1272
- MUST DO:
1273
- - \uBCC0\uACBD\uD55C \uCF54\uB4DC\uC5D0 \uB300\uD574 lint \uC2E4\uD589
1274
- - \uBCC0\uACBD\uD55C \uCF54\uB4DC\uC5D0 \uB300\uD574 formatter \uC2E4\uD589 (lint\uC640 \uBCC4\uAC1C)
1275
- - \uAE30\uC874 \uD14C\uC2A4\uD2B8 \uD1B5\uACFC \uD655\uC778
1276
- - \uC0C8\uB85C \uC791\uC131/\uBCC0\uACBD\uD55C \uCF54\uB4DC\uC758 \uD14C\uC2A4\uD2B8 \uCEE4\uBC84\uB9AC\uC9C0 80% \uC774\uC0C1 \uD655\uC778
1277
- - \uBCC0\uACBD \uBC94\uC704 \uD655\uC778: \uC694\uCCAD\uACFC \uBB34\uAD00\uD55C \uD30C\uC77C\uC744 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uC558\uB294\uC9C0 \uAC80\uC99D
1278
- - \uC2E4\uD328 \uD56D\uBAA9\uC774 \uC788\uC73C\uBA74 \uAD6C\uCCB4\uC801\uC778 \uC5D0\uB7EC \uBA54\uC2DC\uC9C0\uC640 \uD30C\uC77C \uC704\uCE58\uB97C \uBCF4\uACE0
1279
- MUST NOT DO:
1280
- - \uCF54\uB4DC\uB97C \uC9C1\uC811 \uC218\uC815\uD558\uC9C0 \uB9C8\uC138\uC694 (\uBCF4\uACE0\uB9CC)
1281
- - \uD14C\uC2A4\uD2B8\uB97C \uC0AD\uC81C\uD558\uAC70\uB098 \uC2A4\uD0B5\uD558\uC9C0 \uB9C8\uC138\uC694
1282
- CONTEXT: [\uBCC0\uACBD\uD55C \uD30C\uC77C \uBAA9\uB85D\uACFC \uBCC0\uACBD \uB0B4\uC6A9 \uC694\uC57D\uC744 \uC5EC\uAE30\uC5D0 \uD3EC\uD568]
1283
- """
1284
- )
1285
- \`\`\`
1286
-
1287
- \uD004\uB9AC\uD2F0 \uCCB4\uD06C \uC2E4\uD328 \uC2DC: \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8 \uBCF4\uACE0\uB97C \uBC14\uD0D5\uC73C\uB85C \uC9C1\uC811 \uC218\uC815\uD55C \uB4A4, \uB2E4\uC2DC \uC704\uC784\uD558\uC138\uC694.
1288
-
1289
- ### 2. \uC9C0\uC2DD \uC815\uB9AC (\uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8 \uC704\uC784)
1290
-
1291
- \uC791\uC5C5 \uC911 \uAE30\uB85D\uD560 \uB9CC\uD55C \uBC1C\uACAC\uC774 \uC788\uC5C8\uB2E4\uBA74, \uC9C0\uC2DD \uB178\uD2B8 \uC791\uC131\uC744 \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uC5D0 \uC704\uC784\uD558\uC138\uC694.
1222
+ var DEFAULT_TURN_END = `## \uC791\uC5C5 \uB9C8\uBB34\uB9AC
1223
+
1224
+ \uC791\uC5C5\uC774 \uC644\uB8CC\uB418\uBA74 \uC544\uB798 \uD56D\uBAA9\uC744 \uBA54\uC778 \uC5D0\uC774\uC804\uD2B8\uAC00 \uC9C1\uC811 \uD655\uC778\uD558\uC138\uC694.
1225
+
1226
+ ### 1. \uD004\uB9AC\uD2F0 \uCCB4\uD06C
1227
+
1228
+ - \uBCC0\uACBD\uD55C \uCF54\uB4DC\uC5D0 \uB300\uD574 \uD544\uC694\uD55C lint, format, test, build \uAC80\uC99D\uC744 \uC9C1\uC811 \uC2E4\uD589\uD558\uC138\uC694
1229
+ - \uC0C8\uB85C \uC791\uC131\uD558\uAC70\uB098 \uBCC0\uACBD\uD55C \uCF54\uB4DC\uC758 \uCEE4\uBC84\uB9AC\uC9C0 \uAE30\uB300\uCE58\uB97C \uD655\uC778\uD558\uC138\uC694
1230
+ - \uBCC0\uACBD \uBC94\uC704\uB97C \uAC80\uD1A0\uD558\uC5EC \uC694\uCCAD\uACFC \uBB34\uAD00\uD55C \uD30C\uC77C\uC744 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uC558\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694
1231
+ - \uC2E4\uD328 \uD56D\uBAA9\uC774 \uC788\uC73C\uBA74 \uC6D0\uC778, \uC5D0\uB7EC \uBA54\uC2DC\uC9C0, \uAD00\uB828 \uD30C\uC77C \uC704\uCE58\uB97C \uC815\uB9AC\uD55C \uB4A4 \uC9C1\uC811 \uC218\uC815\uD558\uC138\uC694
1232
+ - \uC791\uC5C5\uC774 \uB05D\uB0AC\uB2E4\uACE0 \uD310\uB2E8\uD558\uAE30 \uC804\uC5D0 \uC704 \uAC80\uC99D \uACB0\uACFC\uB97C \uC9C1\uC811 \uB2E4\uC2DC \uD655\uC778\uD558\uC138\uC694
1233
+
1234
+ ### 2. \uC9C0\uC2DD \uC815\uB9AC
1235
+
1236
+ \uC791\uC5C5 \uC911 \uAE30\uB85D\uD560 \uB9CC\uD55C \uBC1C\uACAC\uC774 \uC788\uC5C8\uB2E4\uBA74 \uC9C1\uC811 \uC815\uB9AC\uD558\uC138\uC694.
1292
1237
 
1293
1238
  **\uAE30\uB85D \uB300\uC0C1 \uD310\uB2E8 \uAE30\uC900:**
1294
1239
 
1295
1240
  | \uC0C1\uD669 | \uD15C\uD50C\uB9BF | \uD30C\uC77C\uBA85 \uD328\uD134 |
1296
1241
  | ------------------------------- | --------------------------------------------------- | --------------------------- |
1297
- | \uC544\uD0A4\uD14D\uCC98/\uAE30\uC220 \uC2A4\uD0DD \uC911\uB300 \uACB0\uC815 | [ADR](.opencode/context/templates/adr.md) | \`adr-NNN-\uC81C\uBAA9.md\` |
1298
- | \uBC18\uBCF5 \uC0AC\uC6A9\uD560 \uCF54\uB4DC \uD328\uD134 \uBC1C\uACAC | [Pattern](.opencode/context/templates/pattern.md) | \`pattern-\uC81C\uBAA9.md\` |
1299
- | \uBE44\uC790\uBA85\uD55C \uBC84\uADF8 \uD574\uACB0 | [Bug](.opencode/context/templates/bug.md) | \`bug-\uC81C\uBAA9.md\` |
1300
- | \uC678\uBD80 API/\uB77C\uC774\uBE0C\uB7EC\uB9AC \uC608\uC0C1\uC678 \uB3D9\uC791 | [Gotcha](.opencode/context/templates/gotcha.md) | \`gotcha-\uB77C\uC774\uBE0C\uB7EC\uB9AC-\uC81C\uBAA9.md\` |
1301
- | \uC791\uC740 \uAE30\uC220\uC801 \uC120\uD0DD | [Decision](.opencode/context/templates/decision.md) | \`decision-\uC81C\uBAA9.md\` |
1302
- | \uBAA8\uB4C8/\uD504\uB85C\uC81D\uD2B8 \uAC1C\uC694 \uD544\uC694 | [Context](.opencode/context/templates/context.md) | \`context-\uC81C\uBAA9.md\` |
1303
- | \uBC18\uBCF5 \uAC00\uB2A5\uD55C \uD504\uB85C\uC138\uC2A4 \uC815\uB9BD | [Runbook](.opencode/context/templates/runbook.md) | \`runbook-\uC81C\uBAA9.md\` |
1304
- | \uC2E4\uD5D8/\uB514\uBC84\uAE45 \uC911 \uD559\uC2B5 | [Insight](.opencode/context/templates/insight.md) | \`insight-\uC81C\uBAA9.md\` |
1242
+ | \uC544\uD0A4\uD14D\uCC98/\uAE30\uC220 \uC2A4\uD0DD \uC911\uB300 \uACB0\uC815 | [ADR](.context/templates/adr.md) | \`adr-NNN-\uC81C\uBAA9.md\` |
1243
+ | \uBC18\uBCF5 \uC0AC\uC6A9\uD560 \uCF54\uB4DC \uD328\uD134 \uBC1C\uACAC | [Pattern](.context/templates/pattern.md) | \`pattern-\uC81C\uBAA9.md\` |
1244
+ | \uBE44\uC790\uBA85\uD55C \uBC84\uADF8 \uD574\uACB0 | [Bug](.context/templates/bug.md) | \`bug-\uC81C\uBAA9.md\` |
1245
+ | \uC678\uBD80 API/\uB77C\uC774\uBE0C\uB7EC\uB9AC \uC608\uC0C1\uC678 \uB3D9\uC791 | [Gotcha](.context/templates/gotcha.md) | \`gotcha-\uB77C\uC774\uBE0C\uB7EC\uB9AC-\uC81C\uBAA9.md\` |
1246
+ | \uC791\uC740 \uAE30\uC220\uC801 \uC120\uD0DD | [Decision](.context/templates/decision.md) | \`decision-\uC81C\uBAA9.md\` |
1247
+ | \uBAA8\uB4C8/\uD504\uB85C\uC81D\uD2B8 \uAC1C\uC694 \uD544\uC694 | [Context](.context/templates/context.md) | \`context-\uC81C\uBAA9.md\` |
1248
+ | \uBC18\uBCF5 \uAC00\uB2A5\uD55C \uD504\uB85C\uC138\uC2A4 \uC815\uB9BD | [Runbook](.context/templates/runbook.md) | \`runbook-\uC81C\uBAA9.md\` |
1249
+ | \uC2E4\uD5D8/\uB514\uBC84\uAE45 \uC911 \uD559\uC2B5 | [Insight](.context/templates/insight.md) | \`insight-\uC81C\uBAA9.md\` |
1305
1250
 
1306
1251
  \uD574\uB2F9 \uC0AC\uD56D\uC774 \uC5C6\uC73C\uBA74 \uC774 \uB2E8\uACC4\uB294 \uAC74\uB108\uB6F0\uC138\uC694.
1307
1252
 
1308
- \`\`\`
1309
- task(
1310
- category="quick",
1311
- load_skills=[],
1312
- description="Write Zettelkasten knowledge note",
1313
- prompt="""
1314
- TASK: \uC544\uB798 \uB0B4\uC6A9\uC744 \uBC14\uD0D5\uC73C\uB85C Zettelkasten \uC9C0\uC2DD \uB178\uD2B8\uB97C \uC791\uC131\uD558\uC138\uC694.
1315
- EXPECTED OUTCOME: \uD15C\uD50C\uB9BF\uC5D0 \uB9DE\uB294 \uB178\uD2B8 \uD30C\uC77C \uC0DD\uC131, \uAD00\uB828 \uB178\uD2B8 \uB9C1\uD06C \uC5F0\uACB0
1316
- REQUIRED TOOLS: Read (\uD15C\uD50C\uB9BF \uC77D\uAE30), Write (\uB178\uD2B8 \uC791\uC131), Edit (\uAE30\uC874 \uB178\uD2B8 \uB9C1\uD06C \uCD94\uAC00)
1317
- MUST DO:
1318
- - \uD574\uB2F9 \uD15C\uD50C\uB9BF \uD30C\uC77C\uC744 \uC77D\uACE0 \uADF8 \uAD6C\uC870\uC5D0 \uB9DE\uCDB0 \uB178\uD2B8 \uC791\uC131
1319
- - \uB178\uD2B8 \uCCAB \uC904: \uBA85\uD655\uD55C \uC81C\uBAA9 (# Title)
1320
- - \uD575\uC2EC \uB0B4\uC6A9\uC744 \uC790\uAE30 \uC5B8\uC5B4\uB85C \uAC04\uACB0\uD558\uAC8C \uC11C\uC220 (\uBCF5\uC0AC-\uBD99\uC5EC\uB123\uAE30 \uAE08\uC9C0)
1321
- - \uAD00\uB828 \uB178\uD2B8\uB97C [[relative/path/file.md]] \uD615\uD0DC\uC758 wikilink\uB85C \uC5F0\uACB0
1322
- - knowledge \uB514\uB809\uD1A0\uB9AC (\uAE30\uBCF8: docs/)\uC5D0 \uC800\uC7A5. \uB3C4\uBA54\uC778 \uD3F4\uB354\uAC00 \uC788\uB2E4\uBA74 \uC801\uC808\uD55C \uB3C4\uBA54\uC778\uC5D0 \uC800\uC7A5
1323
- - \uAE30\uC874 \uB178\uD2B8\uC758 \uB0B4\uC6A9\uC774 \uBCC0\uACBD\uC0AC\uD56D\uACFC \uBD88\uC77C\uCE58\uD558\uBA74 \uC5C5\uB370\uC774\uD2B8
1324
- - \uB3C4\uBA54\uC778 \uD3F4\uB354\uC5D0 \uC800\uC7A5\uD588\uB2E4\uBA74 \uD574\uB2F9 INDEX.md\uC5D0 \uD56D\uBAA9 \uCD94\uAC00
1325
- MUST NOT DO:
1326
- - \uC18C\uC2A4 \uCF54\uB4DC\uB97C \uC218\uC815\uD558\uC9C0 \uB9C8\uC138\uC694 (\uB178\uD2B8\uB9CC \uC791\uC131)
1327
- - \uB178\uD2B8\uC5D0 \uC5EC\uB7EC \uC8FC\uC81C\uB97C \uC11E\uC9C0 \uB9C8\uC138\uC694 (\uC6D0\uC790\uC131 \uC6D0\uCE59)
1328
- CONTEXT: [\uAE30\uB85D\uD560 \uBC1C\uACAC \uB0B4\uC6A9, \uD574\uB2F9\uD558\uB294 \uD15C\uD50C\uB9BF \uC885\uB958, \uAD00\uB828 \uAE30\uC874 \uB178\uD2B8 \uBAA9\uB85D\uC744 \uC5EC\uAE30\uC5D0 \uD3EC\uD568]
1329
- """
1330
- )
1331
- \`\`\`
1332
-
1333
- ${"<"}!-- /primary-only -->
1334
- ${"<"}!-- subagent-only -->
1335
- <environment-constraints>
1336
- \uB2F9\uC2E0\uC740 \uD604\uC7AC \uBA54\uC778 \uC624\uCF00\uC2A4\uD2B8\uB808\uC774\uD130\uAC00 \uD638\uCD9C\uD55C **\uB9D0\uB2E8 \uC6CC\uCEE4(Worker) \uC5D0\uC774\uC804\uD2B8**\uC785\uB2C8\uB2E4.
1337
- \uD604\uC7AC \uB2F9\uC2E0\uC758 \uC2E4\uD589 \uD658\uACBD(Sandbox)\uC5D0\uC11C\uB294 \uB124\uD2B8\uC6CC\uD06C \uC790\uC6D0 \uBCF4\uD638\uB97C \uC704\uD574 **\uB2E4\uB978 \uC5D0\uC774\uC804\uD2B8\uB97C \uC0DD\uC131, \uD638\uCD9C, \uC704\uC784\uD558\uB294 \uBAA8\uB4E0 \uB3C4\uAD6C(\uC608: task, background_task \uB4F1)\uC758 \uAD8C\uD55C\uC774 \uC2DC\uC2A4\uD15C \uB808\uBCA8\uC5D0\uC11C \uD68C\uC218**\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
1338
-
1339
- \uB9CC\uC57D \uC791\uC5C5 \uC911 \uB2E4\uB978 \uC804\uBB38\uAC00(explore, librarian \uB4F1)\uC758 \uB3C4\uC6C0\uC774 \uD544\uC694\uD558\uB2E4\uBA74, \uC9C1\uC811 \uC5D0\uC774\uC804\uD2B8\uB97C \uBD80\uB974\uB824 \uC2DC\uB3C4\uD558\uC9C0 \uB9C8\uC138\uC694. \uB300\uC2E0 \uD604\uC7AC\uAE4C\uC9C0\uC758 \uBD84\uC11D \uACB0\uACFC\uB97C \uC694\uC57D\uD558\uACE0 "OOO \uC5D0\uC774\uC804\uD2B8\uC758 \uB3C4\uC6C0\uC774 \uD544\uC694\uD568"\uC774\uB77C\uB294 \uBA54\uC2DC\uC9C0\uC640 \uD568\uAED8 \uC791\uC5C5\uC744 \uC885\uB8CC(Complete)\uD558\uC5EC \uBA54\uC778 \uC5D0\uC774\uC804\uD2B8\uC5D0\uAC8C \uC81C\uC5B4\uAD8C\uC744 \uBC18\uD658\uD558\uC138\uC694.
1340
- </environment-constraints>
1341
- ${"<"}!-- /subagent-only -->
1253
+ - \uAD00\uB828 \uD15C\uD50C\uB9BF \uD30C\uC77C\uC744 \uC77D\uACE0 \uADF8 \uAD6C\uC870\uC5D0 \uB9DE\uCDB0 \uB0B4\uC6A9\uC744 \uC815\uB9AC\uD558\uC138\uC694
1254
+ - \uB178\uD2B8 \uCCAB \uC904\uC740 \uBA85\uD655\uD55C \uC81C\uBAA9(\`# Title\`)\uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694
1255
+ - \uD575\uC2EC \uB0B4\uC6A9\uC744 \uC790\uAE30 \uC5B8\uC5B4\uB85C \uAC04\uACB0\uD558\uAC8C \uC11C\uC220\uD558\uACE0, \uAD00\uB828 \uB178\uD2B8\uB294 \`[[relative/path/file.md]]\` \uD615\uD0DC\uB85C \uC5F0\uACB0\uD558\uC138\uC694
1256
+ - knowledge \uB514\uB809\uD1A0\uB9AC(\`{{knowledgeDir}}/\`) \uB610\uB294 \uC801\uC808\uD55C \uB3C4\uBA54\uC778 \uD3F4\uB354\uC5D0 \uC800\uC7A5\uD558\uACE0, \uD544\uC694\uD55C \uACBD\uC6B0 \uAE30\uC874 INDEX.md\uB098 \uAD00\uB828 \uB178\uD2B8\uB97C \uD568\uAED8 \uAC31\uC2E0\uD558\uC138\uC694
1257
+
1258
+ \uAE30\uC874 \uC124\uCE58\uC758 \uC0AC\uC6A9\uC790 \uD504\uB86C\uD504\uD2B8 \uD30C\uC77C\uC740 \uC790\uB3D9\uC73C\uB85C \uBC14\uB00C\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC0C8 \uAE30\uBCF8 \uD504\uB86C\uD504\uD2B8\uAC00 \uD544\uC694\uD558\uBA74 \`context update prompt\`\uB85C \uBA85\uC2DC\uC801\uC73C\uB85C \uC0C8\uB85C\uACE0\uCE68\uD558\uC138\uC694.
1342
1259
  `;
1343
1260
  var DEFAULT_ADR_TEMPLATE = `# ADR-NNN: [\uC81C\uBAA9]
1344
1261
 
@@ -1558,20 +1475,20 @@ var TEMPLATE_FILES = {
1558
1475
  "index.md": DEFAULT_INDEX_TEMPLATE
1559
1476
  };
1560
1477
  function scaffoldIfNeeded(projectDir) {
1561
- const contextDir = join3(projectDir, ".opencode", "context");
1562
- if (existsSync2(contextDir)) {
1478
+ const contextDir = join4(projectDir, resolveContextDir(projectDir));
1479
+ if (existsSync3(contextDir)) {
1563
1480
  return false;
1564
1481
  }
1565
1482
  try {
1566
- const promptsDir = join3(contextDir, "prompts");
1483
+ const promptsDir = join4(contextDir, "prompts");
1567
1484
  mkdirSync(promptsDir, { recursive: true });
1568
- const templatesDir = join3(contextDir, "templates");
1485
+ const templatesDir = join4(contextDir, "templates");
1569
1486
  mkdirSync(templatesDir, { recursive: true });
1570
- writeFileSync(join3(contextDir, "config.jsonc"), DEFAULT_CONFIG, "utf-8");
1571
- writeFileSync(join3(promptsDir, DEFAULTS.turnStartFile), DEFAULT_TURN_START, "utf-8");
1572
- writeFileSync(join3(promptsDir, DEFAULTS.turnEndFile), DEFAULT_TURN_END, "utf-8");
1487
+ writeFileSync(join4(contextDir, "config.jsonc"), DEFAULT_CONFIG, "utf-8");
1488
+ writeFileSync(join4(promptsDir, DEFAULTS.turnStartFile), DEFAULT_TURN_START, "utf-8");
1489
+ writeFileSync(join4(promptsDir, DEFAULTS.turnEndFile), DEFAULT_TURN_END, "utf-8");
1573
1490
  for (const [filename, content] of Object.entries(TEMPLATE_FILES)) {
1574
- writeFileSync(join3(templatesDir, filename), content, "utf-8");
1491
+ writeFileSync(join4(templatesDir, filename), content, "utf-8");
1575
1492
  }
1576
1493
  writeVersion(contextDir, PLUGIN_VERSION);
1577
1494
  return true;
@@ -1581,25 +1498,25 @@ function scaffoldIfNeeded(projectDir) {
1581
1498
  }
1582
1499
  function getStoredVersion(projectDir) {
1583
1500
  try {
1584
- return readFileSync4(join3(projectDir, ".opencode", "context", ".version"), "utf-8").trim();
1501
+ return readFileSync4(join4(projectDir, resolveContextDir(projectDir), ".version"), "utf-8").trim();
1585
1502
  } catch {
1586
1503
  return null;
1587
1504
  }
1588
1505
  }
1589
1506
  function writeVersion(contextDir, version) {
1590
- writeFileSync(join3(contextDir, ".version"), version, "utf-8");
1507
+ writeFileSync(join4(contextDir, ".version"), version, "utf-8");
1591
1508
  }
1592
1509
  function autoUpdateTemplates(projectDir) {
1593
- const contextDir = join3(projectDir, ".opencode", "context");
1594
- if (!existsSync2(contextDir))
1510
+ const contextDir = join4(projectDir, resolveContextDir(projectDir));
1511
+ if (!existsSync3(contextDir))
1595
1512
  return [];
1596
1513
  const stored = getStoredVersion(projectDir);
1597
1514
  if (stored === PLUGIN_VERSION)
1598
1515
  return [];
1599
- mkdirSync(join3(contextDir, "templates"), { recursive: true });
1516
+ mkdirSync(join4(contextDir, "templates"), { recursive: true });
1600
1517
  const updated = [];
1601
1518
  for (const [filename, content] of Object.entries(TEMPLATE_FILES)) {
1602
- const filePath = join3(contextDir, "templates", filename);
1519
+ const filePath = join4(contextDir, "templates", filename);
1603
1520
  try {
1604
1521
  const existing = readFileSync4(filePath, "utf-8");
1605
1522
  if (existing === content)
@@ -1612,58 +1529,24 @@ function autoUpdateTemplates(projectDir) {
1612
1529
  return updated;
1613
1530
  }
1614
1531
 
1615
- // src/lib/prompt-filter.ts
1616
- function filterByAgentType(content, isSubagent) {
1617
- const process = (text, marker, shouldRemove) => {
1618
- const startTag = `<!-- ${marker} -->`;
1619
- const endTag = `<!-- /${marker} -->`;
1620
- if (!text.includes(startTag))
1621
- return text;
1622
- if (!text.includes(endTag))
1623
- return text;
1624
- const regex = new RegExp(`${startTag}\\n?([\\s\\S]*?)${endTag}\\n?`, "g");
1625
- if (shouldRemove) {
1626
- return text.replace(regex, "");
1627
- } else {
1628
- return text.replace(regex, "$1");
1629
- }
1630
- };
1631
- let filtered = content;
1632
- if (isSubagent) {
1633
- filtered = process(filtered, "primary-only", true);
1634
- filtered = process(filtered, "subagent-only", false);
1635
- } else {
1636
- filtered = process(filtered, "subagent-only", true);
1637
- filtered = process(filtered, "primary-only", false);
1638
- }
1639
- return filtered;
1640
- }
1641
-
1642
- // src/lib/subagent-detector.ts
1643
- async function isSubagentSession(getSession, sessionID, cache) {
1644
- if (!sessionID)
1645
- return false;
1646
- if (cache.has(sessionID))
1647
- return cache.get(sessionID);
1648
- try {
1649
- const session = await getSession(sessionID);
1650
- const result = !!session?.parentID;
1651
- cache.set(sessionID, result);
1652
- return result;
1653
- } catch {
1654
- return false;
1532
+ // src/index.ts
1533
+ function resolvePromptPath(directory, contextDir, promptPath) {
1534
+ if (isAbsolute(promptPath))
1535
+ return promptPath;
1536
+ if (promptPath.startsWith(".context/") || promptPath.startsWith(".opencode/")) {
1537
+ return join5(directory, promptPath);
1655
1538
  }
1539
+ return join5(directory, contextDir, promptPath);
1656
1540
  }
1657
-
1658
- // src/index.ts
1659
1541
  var plugin = async ({ directory, client }) => {
1660
1542
  const scaffolded = scaffoldIfNeeded(directory);
1543
+ const contextDir = resolveContextDir(directory);
1661
1544
  if (scaffolded) {
1662
1545
  await client.app.log({
1663
1546
  body: {
1664
1547
  service: "context",
1665
1548
  level: "info",
1666
- message: "Scaffold created at .opencode/context/"
1549
+ message: `Scaffold created at ${contextDir}/`
1667
1550
  }
1668
1551
  });
1669
1552
  } else {
@@ -1679,35 +1562,17 @@ var plugin = async ({ directory, client }) => {
1679
1562
  }
1680
1563
  }
1681
1564
  const config = loadConfig(directory);
1682
- const subagentCache = new Map;
1683
- const getSession = (id) => client.session.get({ path: { id } });
1684
1565
  return {
1685
- "tool.execute.before": async (input) => {
1686
- try {
1687
- const subagent = await isSubagentSession(getSession, input.sessionID, subagentCache);
1688
- if (subagent) {
1689
- const patterns = config.subagentConfig?.blockedToolPatterns ?? DEFAULTS.blockedToolPatterns;
1690
- const isBlocked = patterns.some((pattern) => new RegExp(pattern, "i").test(input.tool));
1691
- if (isBlocked) {
1692
- throw new Error(`[Security] Subagents are not allowed to use orchestration tools (${input.tool}). Please return control to the main agent.`);
1693
- }
1694
- }
1695
- } catch (err) {
1696
- if (err instanceof Error && err.message.startsWith("[Security]")) {
1697
- throw err;
1698
- }
1699
- }
1700
- },
1701
1566
  "experimental.chat.messages.transform": async (_input, output) => {
1702
1567
  if (output.messages.length === 0)
1703
1568
  return;
1704
1569
  const lastUserMsg = output.messages.filter((m) => m.info.role === "user").at(-1);
1705
1570
  if (!lastUserMsg)
1706
1571
  return;
1707
- const turnStartPath = join4(directory, config.prompts.turnStart ?? join4(DEFAULTS.promptDir, DEFAULTS.turnStartFile));
1708
- const sessionID = lastUserMsg.info.sessionID;
1709
- const subagent = await isSubagentSession(getSession, sessionID, subagentCache);
1710
- const turnStart = filterByAgentType(readPromptFile(turnStartPath) ?? "", subagent);
1572
+ const promptVars = { knowledgeDir: config.knowledge.dir ?? "docs" };
1573
+ const turnStartPath = resolvePromptPath(directory, contextDir, config.prompts.turnStart ?? join5(DEFAULTS.promptDir, DEFAULTS.turnStartFile));
1574
+ const turnStartRaw = readPromptFile(turnStartPath) ?? "";
1575
+ const turnStart = resolvePromptVariables(turnStartRaw, promptVars);
1711
1576
  const knowledgeIndex = buildKnowledgeIndexV2(directory, config.knowledge);
1712
1577
  const indexContent = knowledgeIndex.mode === "flat" ? formatKnowledgeIndex(knowledgeIndex.individualFiles) : formatDomainIndex(knowledgeIndex);
1713
1578
  const combinedContent = [turnStart, indexContent].filter(Boolean).join(`
@@ -1722,11 +1587,11 @@ var plugin = async ({ directory, client }) => {
1722
1587
  text: combinedContent
1723
1588
  });
1724
1589
  }
1725
- const turnEndPath = join4(directory, config.prompts.turnEnd ?? join4(DEFAULTS.promptDir, DEFAULTS.turnEndFile));
1726
- const rawTurnEnd = readPromptFile(turnEndPath);
1727
- const turnEnd = rawTurnEnd ? filterByAgentType(rawTurnEnd, subagent) : null;
1728
- if (!turnEnd)
1590
+ const turnEndPath = resolvePromptPath(directory, contextDir, config.prompts.turnEnd ?? join5(DEFAULTS.promptDir, DEFAULTS.turnEndFile));
1591
+ const turnEndRaw = readPromptFile(turnEndPath);
1592
+ if (!turnEndRaw)
1729
1593
  return;
1594
+ const turnEnd = resolvePromptVariables(turnEndRaw, promptVars);
1730
1595
  const msgId = `context-turn-end-${Date.now()}`;
1731
1596
  output.messages.push({
1732
1597
  info: {