@ksm0709/context 0.0.18 → 0.0.20

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,18 +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
819
  knowledgeSources: ["AGENTS.md"],
820
- templateDir: ".opencode/context/templates",
820
+ templateDir: ".context/templates",
821
821
  indexFilename: "INDEX.md",
822
- maxDomainDepth: 2
822
+ maxDomainDepth: 2,
823
+ knowledgeDir: "docs"
823
824
  };
824
825
  var LIMITS = {
825
826
  maxPromptFileSize: 64 * 1024,
@@ -830,12 +831,27 @@ var LIMITS = {
830
831
  maxIndexFileSize: 32 * 1024
831
832
  };
832
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
+
833
849
  // src/lib/config.ts
834
850
  function getDefaultConfig() {
835
851
  return {
836
852
  prompts: {
837
- turnStart: join(DEFAULTS.promptDir, DEFAULTS.turnStartFile),
838
- turnEnd: join(DEFAULTS.promptDir, DEFAULTS.turnEndFile)
853
+ turnStart: join2(DEFAULTS.promptDir, DEFAULTS.turnStartFile),
854
+ turnEnd: join2(DEFAULTS.promptDir, DEFAULTS.turnEndFile)
839
855
  },
840
856
  knowledge: {
841
857
  dir: "docs",
@@ -843,6 +859,11 @@ function getDefaultConfig() {
843
859
  mode: "auto",
844
860
  indexFilename: DEFAULTS.indexFilename,
845
861
  maxDomainDepth: DEFAULTS.maxDomainDepth
862
+ },
863
+ omx: {
864
+ turnEnd: {
865
+ strategy: "turn-complete-sendkeys"
866
+ }
846
867
  }
847
868
  };
848
869
  }
@@ -859,11 +880,16 @@ function mergeWithDefaults(partial) {
859
880
  mode: partial.knowledge?.mode ?? defaults.knowledge.mode,
860
881
  indexFilename: partial.knowledge?.indexFilename ?? defaults.knowledge.indexFilename,
861
882
  maxDomainDepth: partial.knowledge?.maxDomainDepth ?? defaults.knowledge.maxDomainDepth
883
+ },
884
+ omx: {
885
+ turnEnd: {
886
+ strategy: partial.omx?.turnEnd?.strategy ?? defaults.omx?.turnEnd?.strategy
887
+ }
862
888
  }
863
889
  };
864
890
  }
865
891
  function loadConfig(projectDir) {
866
- const configPath = join(projectDir, DEFAULTS.configPath);
892
+ const configPath = join2(projectDir, resolveContextDir(projectDir), "config.jsonc");
867
893
  try {
868
894
  const raw = readFileSync(configPath, "utf-8");
869
895
  const parsed = parse2(raw);
@@ -876,8 +902,8 @@ function loadConfig(projectDir) {
876
902
  }
877
903
 
878
904
  // src/lib/knowledge-index.ts
879
- import { readdirSync, readFileSync as readFileSync2, statSync, existsSync } from "fs";
880
- import { join as join2, relative, extname } from "path";
905
+ import { readdirSync, readFileSync as readFileSync2, statSync, existsSync as existsSync2 } from "fs";
906
+ import { join as join3, relative, extname } from "path";
881
907
  function extractSummary(filePath) {
882
908
  try {
883
909
  const content = readFileSync2(filePath, "utf-8");
@@ -900,7 +926,7 @@ function scanDir(dir, projectDir, depth, entries) {
900
926
  for (const item of items) {
901
927
  if (entries.length >= LIMITS.maxIndexEntries)
902
928
  break;
903
- const fullPath = join2(dir, item);
929
+ const fullPath = join3(dir, item);
904
930
  try {
905
931
  const stat = statSync(fullPath);
906
932
  if (stat.isDirectory()) {
@@ -920,8 +946,8 @@ function buildKnowledgeIndex(projectDir, sources) {
920
946
  for (const source of sources) {
921
947
  if (entries.length >= LIMITS.maxIndexEntries)
922
948
  break;
923
- const fullPath = join2(projectDir, source);
924
- if (!existsSync(fullPath))
949
+ const fullPath = join3(projectDir, source);
950
+ if (!existsSync2(fullPath))
925
951
  continue;
926
952
  try {
927
953
  const stat = statSync(fullPath);
@@ -950,7 +976,7 @@ function formatKnowledgeIndex(entries) {
950
976
  function countMdFiles(dir, indexFilename) {
951
977
  try {
952
978
  const items = readdirSync(dir);
953
- return items.filter((item) => extname(item) === ".md" && item !== indexFilename && statSync(join2(dir, item)).isFile()).length;
979
+ return items.filter((item) => extname(item) === ".md" && item !== indexFilename && statSync(join3(dir, item)).isFile()).length;
954
980
  } catch {
955
981
  return 0;
956
982
  }
@@ -961,12 +987,12 @@ function scanDomainsRecursive(baseDir, projectDir, indexFilename, currentDepth,
961
987
  try {
962
988
  const items = readdirSync(baseDir);
963
989
  for (const item of items) {
964
- const fullPath = join2(baseDir, item);
990
+ const fullPath = join3(baseDir, item);
965
991
  try {
966
992
  if (!statSync(fullPath).isDirectory())
967
993
  continue;
968
- const indexPath = join2(fullPath, indexFilename);
969
- if (existsSync(indexPath) && statSync(indexPath).isFile()) {
994
+ const indexPath = join3(fullPath, indexFilename);
995
+ if (existsSync2(indexPath) && statSync(indexPath).isFile()) {
970
996
  const rawContent = readFileSync2(indexPath, "utf-8");
971
997
  const indexContent = rawContent.slice(0, LIMITS.maxIndexFileSize);
972
998
  results.push({
@@ -982,8 +1008,8 @@ function scanDomainsRecursive(baseDir, projectDir, indexFilename, currentDepth,
982
1008
  } catch {}
983
1009
  }
984
1010
  function scanDomains(projectDir, knowledgeDir, indexFilename, maxDepth) {
985
- const baseDir = join2(projectDir, knowledgeDir);
986
- if (!existsSync(baseDir))
1011
+ const baseDir = join3(projectDir, knowledgeDir);
1012
+ if (!existsSync2(baseDir))
987
1013
  return [];
988
1014
  const results = [];
989
1015
  scanDomainsRecursive(baseDir, projectDir, indexFilename, 1, maxDepth, results);
@@ -1020,14 +1046,14 @@ function formatDomainIndex(index) {
1020
1046
  `);
1021
1047
  }
1022
1048
  function collectRootFiles(projectDir, knowledgeDir, indexFilename) {
1023
- const baseDir = join2(projectDir, knowledgeDir);
1024
- if (!existsSync(baseDir))
1049
+ const baseDir = join3(projectDir, knowledgeDir);
1050
+ if (!existsSync2(baseDir))
1025
1051
  return [];
1026
1052
  const entries = [];
1027
1053
  try {
1028
1054
  const items = readdirSync(baseDir);
1029
1055
  for (const item of items) {
1030
- const fullPath = join2(baseDir, item);
1056
+ const fullPath = join3(baseDir, item);
1031
1057
  try {
1032
1058
  const stat = statSync(fullPath);
1033
1059
  if (stat.isFile() && extname(item) === ".md" && item !== indexFilename) {
@@ -1056,8 +1082,8 @@ function buildKnowledgeIndexV2(projectDir, knowledgeConfig) {
1056
1082
  const rootFiles = collectRootFiles(projectDir, dir, indexFilename);
1057
1083
  const sourcesEntries = [];
1058
1084
  for (const source of knowledgeConfig.sources) {
1059
- const fullPath = join2(projectDir, source);
1060
- if (!existsSync(fullPath))
1085
+ const fullPath = join3(projectDir, source);
1086
+ if (!existsSync2(fullPath))
1061
1087
  continue;
1062
1088
  try {
1063
1089
  const stat = statSync(fullPath);
@@ -1086,14 +1112,18 @@ function readPromptFile(filePath) {
1086
1112
  return "";
1087
1113
  }
1088
1114
  }
1115
+ function resolvePromptVariables(content, vars) {
1116
+ const normalized = (vars.knowledgeDir || "docs").replace(/\\/g, "/").replace(/\/+$/, "");
1117
+ return content.replaceAll("{{knowledgeDir}}", normalized);
1118
+ }
1089
1119
 
1090
1120
  // src/lib/scaffold.ts
1091
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
1092
- import { join as join3 } from "path";
1121
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
1122
+ import { join as join4 } from "path";
1093
1123
  // package.json
1094
1124
  var package_default = {
1095
1125
  name: "@ksm0709/context",
1096
- version: "0.0.18",
1126
+ version: "0.0.20",
1097
1127
  author: {
1098
1128
  name: "TaehoKang",
1099
1129
  email: "ksm07091@gmail.com"
@@ -1101,27 +1131,32 @@ var package_default = {
1101
1131
  type: "module",
1102
1132
  main: "./dist/index.js",
1103
1133
  bin: {
1104
- context: "./dist/cli/index.js"
1134
+ context: "dist/cli/index.js"
1105
1135
  },
1106
1136
  exports: {
1107
1137
  ".": {
1108
1138
  import: "./dist/index.js",
1109
1139
  types: "./dist/index.d.ts",
1110
1140
  default: "./dist/index.js"
1141
+ },
1142
+ "./omx": {
1143
+ import: "./dist/omx/index.mjs",
1144
+ types: "./dist/omx/index.d.ts",
1145
+ default: "./dist/omx/index.mjs"
1111
1146
  }
1112
1147
  },
1113
1148
  repository: {
1114
1149
  type: "git",
1115
- url: "git@github.com:ksm0709/context.git"
1150
+ url: "git+ssh://git@github.com/ksm0709/context.git"
1116
1151
  },
1117
1152
  publishConfig: {
1118
1153
  access: "public"
1119
1154
  },
1120
1155
  scripts: {
1121
- build: "bun build ./src/index.ts --outdir dist --target bun && bun build ./src/cli/index.ts --outdir dist/cli --target bun",
1156
+ 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",
1122
1157
  test: "vitest run",
1123
1158
  lint: "eslint src --ext .ts",
1124
- prepublishOnly: "bun build ./src/index.ts --outdir dist --target bun && bun build ./src/cli/index.ts --outdir dist/cli --target bun"
1159
+ 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"
1125
1160
  },
1126
1161
  files: [
1127
1162
  "dist"
@@ -1130,7 +1165,6 @@ var package_default = {
1130
1165
  "@opencode-ai/plugin": ">=1.0.0"
1131
1166
  },
1132
1167
  dependencies: {
1133
- "@ksm0709/context": "^0.0.17",
1134
1168
  "jsonc-parser": "^3.0.0"
1135
1169
  },
1136
1170
  devDependencies: {
@@ -1156,12 +1190,18 @@ var DEFAULT_CONFIG = `{
1156
1190
  // Context Plugin Configuration
1157
1191
  // See: https://github.com/ksm0709/context
1158
1192
  "prompts": {
1159
- "turnStart": ".opencode/context/prompts/turn-start.md",
1160
- "turnEnd": ".opencode/context/prompts/turn-end.md"
1193
+ "turnStart": "prompts/turn-start.md",
1194
+ "turnEnd": "prompts/turn-end.md"
1161
1195
  },
1162
1196
  "knowledge": {
1163
1197
  "dir": "docs",
1164
1198
  "sources": ["AGENTS.md"]
1199
+ },
1200
+ "omx": {
1201
+ // Inject turn-end after native turn-complete via tmux send-keys
1202
+ "turnEnd": {
1203
+ "strategy": "turn-complete-sendkeys"
1204
+ }
1165
1205
  }
1166
1206
  }`;
1167
1207
  var DEFAULT_TURN_START = `## Knowledge Context
@@ -1215,21 +1255,21 @@ var DEFAULT_TURN_END = `## \uC791\uC5C5 \uB9C8\uBB34\uB9AC
1215
1255
 
1216
1256
  | \uC0C1\uD669 | \uD15C\uD50C\uB9BF | \uD30C\uC77C\uBA85 \uD328\uD134 |
1217
1257
  | ------------------------------- | --------------------------------------------------- | --------------------------- |
1218
- | \uC544\uD0A4\uD14D\uCC98/\uAE30\uC220 \uC2A4\uD0DD \uC911\uB300 \uACB0\uC815 | [ADR](.opencode/context/templates/adr.md) | \`adr-NNN-\uC81C\uBAA9.md\` |
1219
- | \uBC18\uBCF5 \uC0AC\uC6A9\uD560 \uCF54\uB4DC \uD328\uD134 \uBC1C\uACAC | [Pattern](.opencode/context/templates/pattern.md) | \`pattern-\uC81C\uBAA9.md\` |
1220
- | \uBE44\uC790\uBA85\uD55C \uBC84\uADF8 \uD574\uACB0 | [Bug](.opencode/context/templates/bug.md) | \`bug-\uC81C\uBAA9.md\` |
1221
- | \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\` |
1222
- | \uC791\uC740 \uAE30\uC220\uC801 \uC120\uD0DD | [Decision](.opencode/context/templates/decision.md) | \`decision-\uC81C\uBAA9.md\` |
1223
- | \uBAA8\uB4C8/\uD504\uB85C\uC81D\uD2B8 \uAC1C\uC694 \uD544\uC694 | [Context](.opencode/context/templates/context.md) | \`context-\uC81C\uBAA9.md\` |
1224
- | \uBC18\uBCF5 \uAC00\uB2A5\uD55C \uD504\uB85C\uC138\uC2A4 \uC815\uB9BD | [Runbook](.opencode/context/templates/runbook.md) | \`runbook-\uC81C\uBAA9.md\` |
1225
- | \uC2E4\uD5D8/\uB514\uBC84\uAE45 \uC911 \uD559\uC2B5 | [Insight](.opencode/context/templates/insight.md) | \`insight-\uC81C\uBAA9.md\` |
1258
+ | \uC544\uD0A4\uD14D\uCC98/\uAE30\uC220 \uC2A4\uD0DD \uC911\uB300 \uACB0\uC815 | [ADR](.context/templates/adr.md) | \`adr-NNN-\uC81C\uBAA9.md\` |
1259
+ | \uBC18\uBCF5 \uC0AC\uC6A9\uD560 \uCF54\uB4DC \uD328\uD134 \uBC1C\uACAC | [Pattern](.context/templates/pattern.md) | \`pattern-\uC81C\uBAA9.md\` |
1260
+ | \uBE44\uC790\uBA85\uD55C \uBC84\uADF8 \uD574\uACB0 | [Bug](.context/templates/bug.md) | \`bug-\uC81C\uBAA9.md\` |
1261
+ | \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\` |
1262
+ | \uC791\uC740 \uAE30\uC220\uC801 \uC120\uD0DD | [Decision](.context/templates/decision.md) | \`decision-\uC81C\uBAA9.md\` |
1263
+ | \uBAA8\uB4C8/\uD504\uB85C\uC81D\uD2B8 \uAC1C\uC694 \uD544\uC694 | [Context](.context/templates/context.md) | \`context-\uC81C\uBAA9.md\` |
1264
+ | \uBC18\uBCF5 \uAC00\uB2A5\uD55C \uD504\uB85C\uC138\uC2A4 \uC815\uB9BD | [Runbook](.context/templates/runbook.md) | \`runbook-\uC81C\uBAA9.md\` |
1265
+ | \uC2E4\uD5D8/\uB514\uBC84\uAE45 \uC911 \uD559\uC2B5 | [Insight](.context/templates/insight.md) | \`insight-\uC81C\uBAA9.md\` |
1226
1266
 
1227
1267
  \uD574\uB2F9 \uC0AC\uD56D\uC774 \uC5C6\uC73C\uBA74 \uC774 \uB2E8\uACC4\uB294 \uAC74\uB108\uB6F0\uC138\uC694.
1228
1268
 
1229
1269
  - \uAD00\uB828 \uD15C\uD50C\uB9BF \uD30C\uC77C\uC744 \uC77D\uACE0 \uADF8 \uAD6C\uC870\uC5D0 \uB9DE\uCDB0 \uB0B4\uC6A9\uC744 \uC815\uB9AC\uD558\uC138\uC694
1230
1270
  - \uB178\uD2B8 \uCCAB \uC904\uC740 \uBA85\uD655\uD55C \uC81C\uBAA9(\`# Title\`)\uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694
1231
1271
  - \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
1232
- - knowledge \uB514\uB809\uD1A0\uB9AC(\uAE30\uBCF8: \`docs/\`) \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
1272
+ - 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
1233
1273
 
1234
1274
  \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.
1235
1275
  `;
@@ -1451,20 +1491,20 @@ var TEMPLATE_FILES = {
1451
1491
  "index.md": DEFAULT_INDEX_TEMPLATE
1452
1492
  };
1453
1493
  function scaffoldIfNeeded(projectDir) {
1454
- const contextDir = join3(projectDir, ".opencode", "context");
1455
- if (existsSync2(contextDir)) {
1494
+ const contextDir = join4(projectDir, resolveContextDir(projectDir));
1495
+ if (existsSync3(contextDir)) {
1456
1496
  return false;
1457
1497
  }
1458
1498
  try {
1459
- const promptsDir = join3(contextDir, "prompts");
1499
+ const promptsDir = join4(contextDir, "prompts");
1460
1500
  mkdirSync(promptsDir, { recursive: true });
1461
- const templatesDir = join3(contextDir, "templates");
1501
+ const templatesDir = join4(contextDir, "templates");
1462
1502
  mkdirSync(templatesDir, { recursive: true });
1463
- writeFileSync(join3(contextDir, "config.jsonc"), DEFAULT_CONFIG, "utf-8");
1464
- writeFileSync(join3(promptsDir, DEFAULTS.turnStartFile), DEFAULT_TURN_START, "utf-8");
1465
- writeFileSync(join3(promptsDir, DEFAULTS.turnEndFile), DEFAULT_TURN_END, "utf-8");
1503
+ writeFileSync(join4(contextDir, "config.jsonc"), DEFAULT_CONFIG, "utf-8");
1504
+ writeFileSync(join4(promptsDir, DEFAULTS.turnStartFile), DEFAULT_TURN_START, "utf-8");
1505
+ writeFileSync(join4(promptsDir, DEFAULTS.turnEndFile), DEFAULT_TURN_END, "utf-8");
1466
1506
  for (const [filename, content] of Object.entries(TEMPLATE_FILES)) {
1467
- writeFileSync(join3(templatesDir, filename), content, "utf-8");
1507
+ writeFileSync(join4(templatesDir, filename), content, "utf-8");
1468
1508
  }
1469
1509
  writeVersion(contextDir, PLUGIN_VERSION);
1470
1510
  return true;
@@ -1474,25 +1514,25 @@ function scaffoldIfNeeded(projectDir) {
1474
1514
  }
1475
1515
  function getStoredVersion(projectDir) {
1476
1516
  try {
1477
- return readFileSync4(join3(projectDir, ".opencode", "context", ".version"), "utf-8").trim();
1517
+ return readFileSync4(join4(projectDir, resolveContextDir(projectDir), ".version"), "utf-8").trim();
1478
1518
  } catch {
1479
1519
  return null;
1480
1520
  }
1481
1521
  }
1482
1522
  function writeVersion(contextDir, version) {
1483
- writeFileSync(join3(contextDir, ".version"), version, "utf-8");
1523
+ writeFileSync(join4(contextDir, ".version"), version, "utf-8");
1484
1524
  }
1485
1525
  function autoUpdateTemplates(projectDir) {
1486
- const contextDir = join3(projectDir, ".opencode", "context");
1487
- if (!existsSync2(contextDir))
1526
+ const contextDir = join4(projectDir, resolveContextDir(projectDir));
1527
+ if (!existsSync3(contextDir))
1488
1528
  return [];
1489
1529
  const stored = getStoredVersion(projectDir);
1490
1530
  if (stored === PLUGIN_VERSION)
1491
1531
  return [];
1492
- mkdirSync(join3(contextDir, "templates"), { recursive: true });
1532
+ mkdirSync(join4(contextDir, "templates"), { recursive: true });
1493
1533
  const updated = [];
1494
1534
  for (const [filename, content] of Object.entries(TEMPLATE_FILES)) {
1495
- const filePath = join3(contextDir, "templates", filename);
1535
+ const filePath = join4(contextDir, "templates", filename);
1496
1536
  try {
1497
1537
  const existing = readFileSync4(filePath, "utf-8");
1498
1538
  if (existing === content)
@@ -1506,14 +1546,23 @@ function autoUpdateTemplates(projectDir) {
1506
1546
  }
1507
1547
 
1508
1548
  // src/index.ts
1549
+ function resolvePromptPath(directory, contextDir, promptPath) {
1550
+ if (isAbsolute(promptPath))
1551
+ return promptPath;
1552
+ if (promptPath.startsWith(".context/") || promptPath.startsWith(".opencode/")) {
1553
+ return join5(directory, promptPath);
1554
+ }
1555
+ return join5(directory, contextDir, promptPath);
1556
+ }
1509
1557
  var plugin = async ({ directory, client }) => {
1510
1558
  const scaffolded = scaffoldIfNeeded(directory);
1559
+ const contextDir = resolveContextDir(directory);
1511
1560
  if (scaffolded) {
1512
1561
  await client.app.log({
1513
1562
  body: {
1514
1563
  service: "context",
1515
1564
  level: "info",
1516
- message: "Scaffold created at .opencode/context/"
1565
+ message: `Scaffold created at ${contextDir}/`
1517
1566
  }
1518
1567
  });
1519
1568
  } else {
@@ -1536,8 +1585,10 @@ var plugin = async ({ directory, client }) => {
1536
1585
  const lastUserMsg = output.messages.filter((m) => m.info.role === "user").at(-1);
1537
1586
  if (!lastUserMsg)
1538
1587
  return;
1539
- const turnStartPath = join4(directory, config.prompts.turnStart ?? join4(DEFAULTS.promptDir, DEFAULTS.turnStartFile));
1540
- const turnStart = readPromptFile(turnStartPath) ?? "";
1588
+ const promptVars = { knowledgeDir: config.knowledge.dir ?? "docs" };
1589
+ const turnStartPath = resolvePromptPath(directory, contextDir, config.prompts.turnStart ?? join5(DEFAULTS.promptDir, DEFAULTS.turnStartFile));
1590
+ const turnStartRaw = readPromptFile(turnStartPath) ?? "";
1591
+ const turnStart = resolvePromptVariables(turnStartRaw, promptVars);
1541
1592
  const knowledgeIndex = buildKnowledgeIndexV2(directory, config.knowledge);
1542
1593
  const indexContent = knowledgeIndex.mode === "flat" ? formatKnowledgeIndex(knowledgeIndex.individualFiles) : formatDomainIndex(knowledgeIndex);
1543
1594
  const combinedContent = [turnStart, indexContent].filter(Boolean).join(`
@@ -1552,10 +1603,11 @@ var plugin = async ({ directory, client }) => {
1552
1603
  text: combinedContent
1553
1604
  });
1554
1605
  }
1555
- const turnEndPath = join4(directory, config.prompts.turnEnd ?? join4(DEFAULTS.promptDir, DEFAULTS.turnEndFile));
1556
- const turnEnd = readPromptFile(turnEndPath);
1557
- if (!turnEnd)
1606
+ const turnEndPath = resolvePromptPath(directory, contextDir, config.prompts.turnEnd ?? join5(DEFAULTS.promptDir, DEFAULTS.turnEndFile));
1607
+ const turnEndRaw = readPromptFile(turnEndPath);
1608
+ if (!turnEndRaw)
1558
1609
  return;
1610
+ const turnEnd = resolvePromptVariables(turnEndRaw, promptVars);
1559
1611
  const msgId = `context-turn-end-${Date.now()}`;
1560
1612
  output.messages.push({
1561
1613
  info: {