@ksm0709/context 0.0.35 → 0.1.0-next.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.
package/dist/cli/index.js CHANGED
@@ -27,7 +27,7 @@ function resolveContextDir(projectDir) {
27
27
  // package.json
28
28
  var package_default = {
29
29
  name: "@ksm0709/context",
30
- version: "0.0.35",
30
+ version: "0.1.0-next.1",
31
31
  author: {
32
32
  name: "TaehoKang",
33
33
  email: "ksm07091@gmail.com"
@@ -325,6 +325,8 @@ var DEFAULT_DAILY_NOTE_GUIDE = `# \uB370\uC77C\uB9AC \uB178\uD2B8 \uAE30\uB85D \
325
325
  var DEFAULT_NOTE_GUIDE = `# \uC9C0\uC2DD \uB178\uD2B8 \uC791\uC131 \uBC0F \uAD00\uB9AC \uAC00\uC774\uB4DC
326
326
 
327
327
  - [ ] **\uB178\uD2B8 \uC0DD\uC131**: \`context_mcp_create_knowledge_note\` \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD558\uC5EC \uC0DD\uC131\uD558\uC138\uC694.
328
+ - [ ] \uD15C\uD50C\uB9BF \uBAA8\uB4DC\uC5D0\uC11C\uB294 \uBA3C\uC800 \`.context/templates/<template>.md\`\uB97C \uC77D\uACE0, **\uC644\uC131\uB41C markdown \uC804\uCCB4**\uB97C \`content\`\uB85C \uC804\uB2EC\uD558\uC138\uC694.
329
+ - [ ] \uD15C\uD50C\uB9BF \uBAA8\uB4DC\uC5D0\uC11C\uB294 \`tags\`/\`linked_notes\`\uB97C \uB530\uB85C \uB118\uAE30\uC9C0 \uB9C8\uC138\uC694. \uAD00\uB828 \uB178\uD2B8\uC640 \uBA54\uD0C0\uB370\uC774\uD130\uB294 markdown \uBCF8\uBB38\uC5D0 \uC9C1\uC811 \uC791\uC131\uD574\uC57C \uD569\uB2C8\uB2E4.
328
330
  - [ ] \uC81C\uD154\uCE74\uC2A4\uD150(Zettelkasten) 3\uB300 \uC6D0\uCE59 \uC900\uC218:
329
331
  - [ ] \uC6D0\uC790\uC131: \uD55C \uB178\uD2B8\uB2F9 \uD55C \uC8FC\uC81C
330
332
  - [ ] \uC5F0\uACB0: \uACE0\uB9BD\uB41C \uB178\uD2B8 \uBC29\uC9C0
@@ -2114,10 +2116,16 @@ function registerHook(event, rule) {
2114
2116
  settings.hooks[event] = [];
2115
2117
  }
2116
2118
  const rules = settings.hooks[event];
2119
+ const scriptBasename = (cmd) => {
2120
+ const parts = cmd.trim().split(/\s+/);
2121
+ const last = parts[parts.length - 1];
2122
+ return last.split("/").pop() ?? last;
2123
+ };
2117
2124
  for (const hookCmd of rule.hooks) {
2125
+ const newBasename = scriptBasename(hookCmd.command);
2118
2126
  let replaced = false;
2119
2127
  for (let i = 0;i < rules.length; i++) {
2120
- const existingIdx = rules[i].hooks.findIndex((h) => h.command === hookCmd.command);
2128
+ const existingIdx = rules[i].hooks.findIndex((h) => scriptBasename(h.command) === newBasename);
2121
2129
  if (existingIdx !== -1) {
2122
2130
  rules[i] = rule;
2123
2131
  replaced = true;
package/dist/index.js CHANGED
@@ -25,7 +25,7 @@ import { join as join2 } from "path";
25
25
  // package.json
26
26
  var package_default = {
27
27
  name: "@ksm0709/context",
28
- version: "0.0.35",
28
+ version: "0.1.0-next.1",
29
29
  author: {
30
30
  name: "TaehoKang",
31
31
  email: "ksm07091@gmail.com"
@@ -323,6 +323,8 @@ var DEFAULT_DAILY_NOTE_GUIDE = `# \uB370\uC77C\uB9AC \uB178\uD2B8 \uAE30\uB85D \
323
323
  var DEFAULT_NOTE_GUIDE = `# \uC9C0\uC2DD \uB178\uD2B8 \uC791\uC131 \uBC0F \uAD00\uB9AC \uAC00\uC774\uB4DC
324
324
 
325
325
  - [ ] **\uB178\uD2B8 \uC0DD\uC131**: \`context_mcp_create_knowledge_note\` \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD558\uC5EC \uC0DD\uC131\uD558\uC138\uC694.
326
+ - [ ] \uD15C\uD50C\uB9BF \uBAA8\uB4DC\uC5D0\uC11C\uB294 \uBA3C\uC800 \`.context/templates/<template>.md\`\uB97C \uC77D\uACE0, **\uC644\uC131\uB41C markdown \uC804\uCCB4**\uB97C \`content\`\uB85C \uC804\uB2EC\uD558\uC138\uC694.
327
+ - [ ] \uD15C\uD50C\uB9BF \uBAA8\uB4DC\uC5D0\uC11C\uB294 \`tags\`/\`linked_notes\`\uB97C \uB530\uB85C \uB118\uAE30\uC9C0 \uB9C8\uC138\uC694. \uAD00\uB828 \uB178\uD2B8\uC640 \uBA54\uD0C0\uB370\uC774\uD130\uB294 markdown \uBCF8\uBB38\uC5D0 \uC9C1\uC811 \uC791\uC131\uD574\uC57C \uD569\uB2C8\uB2E4.
326
328
  - [ ] \uC81C\uD154\uCE74\uC2A4\uD150(Zettelkasten) 3\uB300 \uC6D0\uCE59 \uC900\uC218:
327
329
  - [ ] \uC6D0\uC790\uC131: \uD55C \uB178\uD2B8\uB2F9 \uD55C \uC8FC\uC81C
328
330
  - [ ] \uC5F0\uACB0: \uACE0\uB9BD\uB41C \uB178\uD2B8 \uBC29\uC9C0
package/dist/mcp.js CHANGED
@@ -4,43 +4,25 @@ var __getProtoOf = Object.getPrototypeOf;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- function __accessProp(key) {
8
- return this[key];
9
- }
10
- var __toESMCache_node;
11
- var __toESMCache_esm;
12
7
  var __toESM = (mod, isNodeMode, target) => {
13
- var canCache = mod != null && typeof mod === "object";
14
- if (canCache) {
15
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
- var cached = cache.get(mod);
17
- if (cached)
18
- return cached;
19
- }
20
8
  target = mod != null ? __create(__getProtoOf(mod)) : {};
21
9
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
10
  for (let key of __getOwnPropNames(mod))
23
11
  if (!__hasOwnProp.call(to, key))
24
12
  __defProp(to, key, {
25
- get: __accessProp.bind(mod, key),
13
+ get: () => mod[key],
26
14
  enumerable: true
27
15
  });
28
- if (canCache)
29
- cache.set(mod, to);
30
16
  return to;
31
17
  };
32
18
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
- var __returnValue = (v) => v;
34
- function __exportSetter(name, newValue) {
35
- this[name] = __returnValue.bind(null, newValue);
36
- }
37
19
  var __export = (target, all) => {
38
20
  for (var name in all)
39
21
  __defProp(target, name, {
40
22
  get: all[name],
41
23
  enumerable: true,
42
24
  configurable: true,
43
- set: __exportSetter.bind(all, name)
25
+ set: (newValue) => all[name] = () => newValue
44
26
  });
45
27
  };
46
28
 
@@ -6303,7 +6285,7 @@ var require_formats = __commonJS((exports) => {
6303
6285
  }
6304
6286
  var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
6305
6287
  function getTime(strictTimeZone) {
6306
- return function time3(str) {
6288
+ return function time(str) {
6307
6289
  const matches = TIME.exec(str);
6308
6290
  if (!matches)
6309
6291
  return false;
@@ -33093,6 +33075,100 @@ function stripExtension(file2) {
33093
33075
  return file2.endsWith(".md") ? file2.slice(0, -3) : file2;
33094
33076
  }
33095
33077
 
33078
+ // src/lib/knowledge-note-template-validation.ts
33079
+ var HEADING_REGEX2 = /^(#{1,3})\s+(.+)$/;
33080
+ var ADDITIONAL_FORBIDDEN_SNIPPETS = ["[\uC81C\uBAA9]", "[\uAC04\uB2E8\uD55C \uC124\uBA85]", "TODO"];
33081
+ var RELATED_NOTES_TITLES = ["\uAD00\uB828 \uB178\uD2B8", "Related Notes"];
33082
+ function escapeRegExp(value) {
33083
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
33084
+ }
33085
+ function parseMarkdownHeadings(content) {
33086
+ const lines = content.split(`
33087
+ `);
33088
+ return lines.flatMap((line, index) => {
33089
+ const match = line.match(HEADING_REGEX2);
33090
+ if (!match) {
33091
+ return [];
33092
+ }
33093
+ return [
33094
+ {
33095
+ level: match[1].length,
33096
+ line: index,
33097
+ raw: line.trim(),
33098
+ text: match[2].trim()
33099
+ }
33100
+ ];
33101
+ });
33102
+ }
33103
+ function buildHeadingPattern(templateText) {
33104
+ const escaped = escapeRegExp(templateText).replace(/\\\[[^\]]+\\\]/g, "(.+)");
33105
+ return new RegExp(`^${escaped}$`);
33106
+ }
33107
+ function headingMatches(template, candidate) {
33108
+ if (template.level !== candidate.level) {
33109
+ return false;
33110
+ }
33111
+ return buildHeadingPattern(template.text).test(candidate.text);
33112
+ }
33113
+ function collectForbiddenSnippets(templateContent) {
33114
+ const snippets = new Set(ADDITIONAL_FORBIDDEN_SNIPPETS);
33115
+ for (const line of templateContent.split(`
33116
+ `)) {
33117
+ const trimmed = line.trim();
33118
+ if (!trimmed) {
33119
+ continue;
33120
+ }
33121
+ const headingMatch = trimmed.match(HEADING_REGEX2);
33122
+ if (headingMatch) {
33123
+ if (trimmed.includes("[") || trimmed.includes("...") || trimmed.includes("TODO")) {
33124
+ snippets.add(trimmed);
33125
+ }
33126
+ continue;
33127
+ }
33128
+ snippets.add(trimmed);
33129
+ }
33130
+ return [...snippets];
33131
+ }
33132
+ function isRelatedNotesHeading(heading) {
33133
+ return RELATED_NOTES_TITLES.includes(heading.text);
33134
+ }
33135
+ function validateTemplatedKnowledgeNoteContent(templateContent, content) {
33136
+ const templateHeadings = parseMarkdownHeadings(templateContent);
33137
+ const contentHeadings = parseMarkdownHeadings(content);
33138
+ const contentLines = content.split(`
33139
+ `);
33140
+ const errors3 = [];
33141
+ const matchedHeadings = [];
33142
+ let searchStart = 0;
33143
+ for (const templateHeading of templateHeadings) {
33144
+ const matchIndex = contentHeadings.findIndex((candidate, index) => index >= searchStart && headingMatches(templateHeading, candidate));
33145
+ if (matchIndex === -1) {
33146
+ errors3.push(`Missing required heading: ${templateHeading.raw}`);
33147
+ continue;
33148
+ }
33149
+ matchedHeadings.push(contentHeadings[matchIndex]);
33150
+ searchStart = matchIndex + 1;
33151
+ }
33152
+ for (const forbiddenSnippet of collectForbiddenSnippets(templateContent)) {
33153
+ if (content.includes(forbiddenSnippet)) {
33154
+ errors3.push(`Template placeholder was not replaced: ${forbiddenSnippet}`);
33155
+ }
33156
+ }
33157
+ matchedHeadings.forEach((heading, index) => {
33158
+ const nextHeadingLine = matchedHeadings[index + 1]?.line ?? contentLines.length;
33159
+ const sectionBody = contentLines.slice(heading.line + 1, nextHeadingLine).join(`
33160
+ `).trim();
33161
+ if (heading.level > 1 && !sectionBody) {
33162
+ errors3.push(`Section is empty: ${heading.raw}`);
33163
+ return;
33164
+ }
33165
+ if (isRelatedNotesHeading(heading) && !/\[\[[^[\]]+\]\]/.test(sectionBody)) {
33166
+ errors3.push(`Related notes section must include at least one wikilink: ${heading.raw}`);
33167
+ }
33168
+ });
33169
+ return { errors: errors3 };
33170
+ }
33171
+
33096
33172
  // src/lib/mcp-server.ts
33097
33173
  function startMcpServer() {
33098
33174
  const server = new McpServer({
@@ -33283,13 +33359,13 @@ function startMcpServer() {
33283
33359
  }
33284
33360
  });
33285
33361
  server.registerTool("create_knowledge_note", {
33286
- description: "Create a new Zettelkasten knowledge note with frontmatter and wikilinks. You can optionally use a template by providing the `template` parameter. Available templates: adr (Architecture Decision Records), pattern (Design patterns), bug (Bug reports and analysis), gotcha (Pitfalls and gotchas), decision (General decisions), context (General context and background), runbook (Procedures and runbooks), insight (Insights and learnings).",
33362
+ description: "Create a new Zettelkasten knowledge note with frontmatter and wikilinks. When you provide `template`, first read `.context/templates/<template>.md` and pass fully completed markdown in `content`. Available templates: adr (Architecture Decision Records), pattern (Design patterns), bug (Bug reports and analysis), gotcha (Pitfalls and gotchas), decision (General decisions), context (General context and background), runbook (Procedures and runbooks), insight (Insights and learnings).",
33287
33363
  inputSchema: {
33288
33364
  title: exports_external.string().describe("The title of the note"),
33289
- content: exports_external.string().describe("The main content of the note"),
33290
- tags: exports_external.array(exports_external.string()).optional().describe("Optional tags for the note"),
33291
- linked_notes: exports_external.array(exports_external.string()).optional().describe("Optional list of related note titles to link to"),
33292
- template: exports_external.enum(["adr", "pattern", "bug", "gotcha", "decision", "context", "runbook", "insight"]).optional().describe("Optional template to use for the note")
33365
+ content: exports_external.string().describe("The main content of the note. When `template` is set, this must be the complete markdown document that already follows the template."),
33366
+ tags: exports_external.array(exports_external.string()).optional().describe("Optional tags for the note. Not supported when `template` is set."),
33367
+ linked_notes: exports_external.array(exports_external.string()).optional().describe("Optional list of related note titles to link to. Not supported when `template` is set; include related notes directly in the markdown content instead."),
33368
+ template: exports_external.enum(["adr", "pattern", "bug", "gotcha", "decision", "context", "runbook", "insight"]).optional().describe("Optional template to validate against. Read the template file first and pass fully completed markdown in `content`.")
33293
33369
  }
33294
33370
  }, async ({ title, content, tags, linked_notes, template }) => {
33295
33371
  try {
@@ -33300,18 +33376,48 @@ function startMcpServer() {
33300
33376
  const date6 = new Date().toISOString().split("T")[0];
33301
33377
  let fileContent = "";
33302
33378
  if (template) {
33379
+ if (tags && tags.length > 0) {
33380
+ return {
33381
+ content: [
33382
+ {
33383
+ type: "text",
33384
+ text: "Error creating knowledge note: `tags` is not supported in template mode. Read the template and include any frontmatter or metadata directly in the markdown content."
33385
+ }
33386
+ ],
33387
+ isError: true
33388
+ };
33389
+ }
33390
+ if (linked_notes && linked_notes.length > 0) {
33391
+ return {
33392
+ content: [
33393
+ {
33394
+ type: "text",
33395
+ text: "Error creating knowledge note: `linked_notes` is not supported in template mode. Read the template and include related notes directly in the markdown content."
33396
+ }
33397
+ ],
33398
+ isError: true
33399
+ };
33400
+ }
33303
33401
  const templatePath = path2.resolve(process.cwd(), `.context/templates/${template}.md`);
33304
- try {
33305
- fileContent = await fs2.readFile(templatePath, "utf-8");
33306
- fileContent = fileContent.replace(/\[\uC81C\uBAA9\]/g, title);
33307
- fileContent += `
33308
-
33309
- ${content}`;
33310
- } catch (err) {
33311
- fileContent = `Error loading template: ${err instanceof Error ? err.message : String(err)}
33312
-
33313
- ${content}`;
33402
+ const templateContent = await fs2.readFile(templatePath, "utf-8");
33403
+ const validation = validateTemplatedKnowledgeNoteContent(templateContent, content);
33404
+ if (validation.errors.length > 0) {
33405
+ return {
33406
+ content: [
33407
+ {
33408
+ type: "text",
33409
+ text: `Error creating knowledge note: template content is invalid.
33410
+ - ${validation.errors.join(`
33411
+ - `)}
33412
+ Read the template and provide the fully completed markdown document in \`content\`.`
33413
+ }
33414
+ ],
33415
+ isError: true
33416
+ };
33314
33417
  }
33418
+ fileContent = content.endsWith(`
33419
+ `) ? content : `${content}
33420
+ `;
33315
33421
  } else {
33316
33422
  fileContent = `---
33317
33423
  `;
@@ -20,7 +20,7 @@ function resolveContextDir(projectDir) {
20
20
  // package.json
21
21
  var package_default = {
22
22
  name: "@ksm0709/context",
23
- version: "0.0.35",
23
+ version: "0.1.0-next.1",
24
24
  author: {
25
25
  name: "TaehoKang",
26
26
  email: "ksm07091@gmail.com"
@@ -318,6 +318,8 @@ var DEFAULT_DAILY_NOTE_GUIDE = `# \uB370\uC77C\uB9AC \uB178\uD2B8 \uAE30\uB85D \
318
318
  var DEFAULT_NOTE_GUIDE = `# \uC9C0\uC2DD \uB178\uD2B8 \uC791\uC131 \uBC0F \uAD00\uB9AC \uAC00\uC774\uB4DC
319
319
 
320
320
  - [ ] **\uB178\uD2B8 \uC0DD\uC131**: \`context_mcp_create_knowledge_note\` \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD558\uC5EC \uC0DD\uC131\uD558\uC138\uC694.
321
+ - [ ] \uD15C\uD50C\uB9BF \uBAA8\uB4DC\uC5D0\uC11C\uB294 \uBA3C\uC800 \`.context/templates/<template>.md\`\uB97C \uC77D\uACE0, **\uC644\uC131\uB41C markdown \uC804\uCCB4**\uB97C \`content\`\uB85C \uC804\uB2EC\uD558\uC138\uC694.
322
+ - [ ] \uD15C\uD50C\uB9BF \uBAA8\uB4DC\uC5D0\uC11C\uB294 \`tags\`/\`linked_notes\`\uB97C \uB530\uB85C \uB118\uAE30\uC9C0 \uB9C8\uC138\uC694. \uAD00\uB828 \uB178\uD2B8\uC640 \uBA54\uD0C0\uB370\uC774\uD130\uB294 markdown \uBCF8\uBB38\uC5D0 \uC9C1\uC811 \uC791\uC131\uD574\uC57C \uD569\uB2C8\uB2E4.
321
323
  - [ ] \uC81C\uD154\uCE74\uC2A4\uD150(Zettelkasten) 3\uB300 \uC6D0\uCE59 \uC900\uC218:
322
324
  - [ ] \uC6D0\uC790\uC131: \uD55C \uB178\uD2B8\uB2F9 \uD55C \uC8FC\uC81C
323
325
  - [ ] \uC5F0\uACB0: \uACE0\uB9BD\uB41C \uB178\uD2B8 \uBC29\uC9C0
@@ -105,7 +105,7 @@ import { join as join3 } from "node:path";
105
105
  // package.json
106
106
  var package_default = {
107
107
  name: "@ksm0709/context",
108
- version: "0.0.35",
108
+ version: "0.1.0-next.1",
109
109
  author: {
110
110
  name: "TaehoKang",
111
111
  email: "ksm07091@gmail.com"
@@ -403,6 +403,8 @@ var DEFAULT_DAILY_NOTE_GUIDE = `# 데일리 노트 기록 가이드
403
403
  var DEFAULT_NOTE_GUIDE = `# 지식 노트 작성 및 관리 가이드
404
404
 
405
405
  - [ ] **노트 생성**: \`context_mcp_create_knowledge_note\` 도구를 사용하여 생성하세요.
406
+ - [ ] 템플릿 모드에서는 먼저 \`.context/templates/<template>.md\`를 읽고, **완성된 markdown 전체**를 \`content\`로 전달하세요.
407
+ - [ ] 템플릿 모드에서는 \`tags\`/\`linked_notes\`를 따로 넘기지 마세요. 관련 노트와 메타데이터는 markdown 본문에 직접 작성해야 합니다.
406
408
  - [ ] 제텔카스텐(Zettelkasten) 3대 원칙 준수:
407
409
  - [ ] 원자성: 한 노트당 한 주제
408
410
  - [ ] 연결: 고립된 노트 방지
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ksm0709/context",
3
- "version": "0.0.35",
3
+ "version": "0.1.0-next.1",
4
4
  "author": {
5
5
  "name": "TaehoKang",
6
6
  "email": "ksm07091@gmail.com"