spets 0.2.0 → 0.2.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.
@@ -12,6 +12,8 @@ import { join } from "path";
12
12
  import matter from "gray-matter";
13
13
  var KNOWLEDGE_DIR = "knowledge";
14
14
  var GUIDE_FILE = "guide.md";
15
+ var MAX_SUMMARY_CHARS = 2e3;
16
+ var MAX_KNOWLEDGE_CHARS = 8e3;
15
17
  function getKnowledgeDir(cwd = process.cwd()) {
16
18
  return join(getSpetsDir(cwd), KNOWLEDGE_DIR);
17
19
  }
@@ -58,10 +60,21 @@ function saveKnowledge(filename, content, source, cwd = process.cwd()) {
58
60
  ensureKnowledgeDir(cwd);
59
61
  const knowledgeDir = getKnowledgeDir(cwd);
60
62
  const filePath = join(knowledgeDir, `${filename}.md`);
63
+ let createdAt = (/* @__PURE__ */ new Date()).toISOString();
64
+ if (existsSync(filePath)) {
65
+ const existing = readFileSync(filePath, "utf-8");
66
+ const { data } = matter(existing);
67
+ if (data.createdAt) {
68
+ createdAt = data.createdAt;
69
+ }
70
+ }
61
71
  const frontmatter = {
62
72
  source,
63
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
73
+ createdAt
64
74
  };
75
+ if (existsSync(filePath)) {
76
+ frontmatter.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
77
+ }
65
78
  const fileContent = matter.stringify(content, frontmatter);
66
79
  writeFileSync(filePath, fileContent);
67
80
  }
@@ -75,21 +88,69 @@ function loadGuide(cwd = process.cwd()) {
75
88
  const { content } = matter(fileContent);
76
89
  return content.trim();
77
90
  }
78
- function buildAvailableKnowledgeSection(cwd = process.cwd()) {
91
+ function knowledgePriority(filename) {
92
+ if (filename.startsWith("rule--")) return 0;
93
+ if (filename.startsWith("pattern--")) return 1;
94
+ if (filename.startsWith("pref--")) return 2;
95
+ return 3;
96
+ }
97
+ function buildKnowledgeSummarySection(cwd = process.cwd()) {
79
98
  const files = listKnowledgeFiles(cwd);
80
99
  if (files.length === 0) {
81
100
  return "";
82
101
  }
102
+ const sorted = [...files].sort((a, b) => knowledgePriority(a) - knowledgePriority(b));
103
+ const lines = [];
104
+ let charCount = 0;
105
+ let truncated = 0;
106
+ for (const filename of sorted) {
107
+ const entry = loadKnowledgeFile(filename, cwd);
108
+ const firstLine = entry?.content.split("\n")[0]?.trim() || "";
109
+ const line = `- ${filename}: ${firstLine}`;
110
+ if (charCount + line.length > MAX_SUMMARY_CHARS) {
111
+ truncated = sorted.length - lines.length;
112
+ break;
113
+ }
114
+ lines.push(line);
115
+ charCount += line.length;
116
+ }
83
117
  const parts = [];
84
- parts.push("## Available Knowledge");
118
+ parts.push("## Knowledge Summary");
85
119
  parts.push("");
86
- parts.push("\uB2E4\uC74C \uC9C0\uC2DD \uD30C\uC77C\uB4E4\uC774 \uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC800\uC7A5\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4:");
87
- for (const file of files) {
88
- parts.push(`- ${file}`);
120
+ parts.push("\uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC800\uC7A5\uB41C \uC9C0\uC2DD:");
121
+ parts.push("");
122
+ parts.push(...lines);
123
+ if (truncated > 0) {
124
+ parts.push(`- ... and ${truncated} more`);
89
125
  }
90
126
  parts.push("");
91
- parts.push("\uD604\uC7AC \uD0DC\uC2A4\uD06C\uC5D0 \uCC38\uACE0\uD560 \uC9C0\uC2DD\uC774 \uC788\uB2E4\uBA74 JSON \uCD9C\uB825\uC758 `knowledgeRequests` \uD544\uB4DC\uC5D0 \uD30C\uC77C\uBA85\uC744 \uCD94\uAC00\uD558\uC138\uC694.");
127
+ return parts.join("\n");
128
+ }
129
+ function buildBudgetedKnowledgeSection(entries, heading = "## Loaded Knowledge") {
130
+ if (entries.length === 0) {
131
+ return "";
132
+ }
133
+ const parts = [];
134
+ parts.push(heading);
92
135
  parts.push("");
136
+ let charCount = 0;
137
+ for (const entry of entries) {
138
+ const section = `### ${entry.filename}
139
+
140
+ ${entry.content}
141
+ `;
142
+ if (charCount + section.length > MAX_KNOWLEDGE_CHARS) {
143
+ const remaining = entries.length - parts.filter((p) => p.startsWith("### ")).length;
144
+ if (remaining > 0) {
145
+ parts.push(`
146
+ *... ${remaining} more entries truncated due to budget*
147
+ `);
148
+ }
149
+ break;
150
+ }
151
+ parts.push(section);
152
+ charCount += section.length;
153
+ }
93
154
  return parts.join("\n");
94
155
  }
95
156
 
@@ -210,7 +271,8 @@ export {
210
271
  loadKnowledgeFiles,
211
272
  saveKnowledge,
212
273
  loadGuide,
213
- buildAvailableKnowledgeSection,
274
+ buildKnowledgeSummarySection,
275
+ buildBudgetedKnowledgeSection,
214
276
  renderKnowledgeJSON,
215
277
  runKnowledgeInteractive
216
278
  };
package/dist/index.js CHANGED
@@ -22,7 +22,8 @@ import {
22
22
  runDocsInteractive
23
23
  } from "./chunk-3JIHGW47.js";
24
24
  import {
25
- buildAvailableKnowledgeSection,
25
+ buildBudgetedKnowledgeSection,
26
+ buildKnowledgeSummarySection,
26
27
  getKnowledgeDir,
27
28
  listKnowledgeFiles,
28
29
  loadGuide,
@@ -31,7 +32,7 @@ import {
31
32
  renderKnowledgeJSON,
32
33
  runKnowledgeInteractive,
33
34
  saveKnowledge
34
- } from "./chunk-3QH66XVU.js";
35
+ } from "./chunk-OIFVTELS.js";
35
36
  import {
36
37
  clearConfigCache,
37
38
  getConfigPath,
@@ -1045,7 +1046,7 @@ function buildExplorePrompt(params) {
1045
1046
  parts.push("");
1046
1047
  const config = loadConfig(cwd);
1047
1048
  if (config.knowledge?.inject !== false) {
1048
- const knowledgeSection = buildAvailableKnowledgeSection(cwd);
1049
+ const knowledgeSection = buildKnowledgeSummarySection(cwd);
1049
1050
  if (knowledgeSection) {
1050
1051
  parts.push(knowledgeSection);
1051
1052
  }
@@ -1125,6 +1126,7 @@ function buildExploreTeamSection() {
1125
1126
 
1126
1127
  // src/core/prompts/clarify.ts
1127
1128
  function buildClarifyPrompt(params) {
1129
+ const cwd = params.cwd || process.cwd();
1128
1130
  const parts = [];
1129
1131
  parts.push("# Clarify Phase");
1130
1132
  parts.push("");
@@ -1141,6 +1143,13 @@ function buildClarifyPrompt(params) {
1141
1143
  parts.push("");
1142
1144
  parts.push(params.gatheredContext);
1143
1145
  parts.push("");
1146
+ const config = loadConfig(cwd);
1147
+ if (config.knowledge?.inject !== false) {
1148
+ const knowledgeSection = buildKnowledgeSummarySection(cwd);
1149
+ if (knowledgeSection) {
1150
+ parts.push(knowledgeSection);
1151
+ }
1152
+ }
1144
1153
  if (params.loadedKnowledge && params.loadedKnowledge.length > 0) {
1145
1154
  parts.push("## Requested Knowledge");
1146
1155
  parts.push("");
@@ -1408,6 +1417,12 @@ function buildExecutePrompt(params) {
1408
1417
  }
1409
1418
  parts.push("");
1410
1419
  }
1420
+ if (config.knowledge?.inject !== false) {
1421
+ const knowledgeSection = buildKnowledgeSummarySection(cwd);
1422
+ if (knowledgeSection) {
1423
+ parts.push(knowledgeSection);
1424
+ }
1425
+ }
1411
1426
  if (params.loadedKnowledge && params.loadedKnowledge.length > 0) {
1412
1427
  parts.push("### Referenced Knowledge");
1413
1428
  parts.push("");
@@ -1518,6 +1533,20 @@ function buildVerifyPrompt(params) {
1518
1533
  parts.push(template);
1519
1534
  parts.push("");
1520
1535
  }
1536
+ const config = loadConfig(cwd);
1537
+ if (config.knowledge?.inject !== false) {
1538
+ const knowledgeSection = buildKnowledgeSummarySection(cwd);
1539
+ if (knowledgeSection) {
1540
+ parts.push(knowledgeSection);
1541
+ }
1542
+ }
1543
+ if (params.loadedKnowledge && params.loadedKnowledge.length > 0) {
1544
+ const knowledgeContent = buildBudgetedKnowledgeSection(params.loadedKnowledge);
1545
+ if (knowledgeContent) {
1546
+ parts.push(knowledgeContent);
1547
+ parts.push("");
1548
+ }
1549
+ }
1521
1550
  parts.push("## Verification Criteria");
1522
1551
  parts.push("");
1523
1552
  parts.push("1. **Accuracy** (0-100)");
@@ -1542,7 +1571,6 @@ function buildVerifyPrompt(params) {
1542
1571
  parts.push("");
1543
1572
  parts.push("If it fails, the output will be automatically revised (up to 3 attempts).");
1544
1573
  parts.push("");
1545
- const config = loadConfig(cwd);
1546
1574
  if (config.team?.enabled) {
1547
1575
  parts.push(buildVerifyTeamSection());
1548
1576
  }
@@ -1663,6 +1691,20 @@ function buildKnowledgeExtractPrompt(params) {
1663
1691
  parts.push("");
1664
1692
  }
1665
1693
  }
1694
+ const cwd = params.cwd || process.cwd();
1695
+ const existingFiles = listKnowledgeFiles(cwd);
1696
+ if (existingFiles.length > 0) {
1697
+ parts.push("## Existing Knowledge Entries");
1698
+ parts.push("");
1699
+ parts.push("\uB2E4\uC74C \uC9C0\uC2DD\uC774 \uC774\uBBF8 \uC800\uC7A5\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4. \uACB9\uCE58\uB294 \uB0B4\uC6A9\uC740 update, \uC0C8\uB85C\uC6B4 \uAC83\uB9CC create \uD558\uC138\uC694:");
1700
+ parts.push("");
1701
+ for (const filename of existingFiles) {
1702
+ const entry = loadKnowledgeFile(filename, cwd);
1703
+ const firstLine = entry?.content.split("\n")[0]?.trim() || "";
1704
+ parts.push(`- **${filename}**: ${firstLine}`);
1705
+ }
1706
+ parts.push("");
1707
+ }
1666
1708
  parts.push("## Your Task");
1667
1709
  parts.push("");
1668
1710
  parts.push("Extract knowledge that would be useful for future workflows:");
@@ -1678,6 +1720,10 @@ function buildKnowledgeExtractPrompt(params) {
1678
1720
  parts.push("- One-time decisions");
1679
1721
  parts.push("- Temporary workarounds");
1680
1722
  parts.push("");
1723
+ parts.push("**Update or Create:**");
1724
+ parts.push('- If an existing entry covers the same topic, use `"action": "update"` with the same filename');
1725
+ parts.push('- Only use `"action": "create"` for genuinely new knowledge');
1726
+ parts.push("");
1681
1727
  parts.push("## Output Format");
1682
1728
  parts.push("");
1683
1729
  parts.push("Output a JSON object with this structure:");
@@ -1688,7 +1734,8 @@ function buildKnowledgeExtractPrompt(params) {
1688
1734
  parts.push(" {");
1689
1735
  parts.push(' "filename": "pattern--error-handling",');
1690
1736
  parts.push(' "content": "The knowledge content in markdown format...",');
1691
- parts.push(' "reason": "Why this is worth saving"');
1737
+ parts.push(' "reason": "Why this is worth saving",');
1738
+ parts.push(' "action": "create"');
1692
1739
  parts.push(" }");
1693
1740
  parts.push(" ]");
1694
1741
  parts.push("}");
@@ -1842,6 +1889,7 @@ function buildPhaseVerifyResponse(cwd, state) {
1842
1889
  totalSteps: state.totalSteps,
1843
1890
  documentPath,
1844
1891
  verifyAttempts: state.verifyAttempts || 1,
1892
+ loadedKnowledge: state.loadedKnowledge?.map((k) => ({ filename: k.filename, content: k.content })),
1845
1893
  cwd
1846
1894
  });
1847
1895
  return {
@@ -2446,7 +2494,8 @@ function parseKnowledgeOutput(response) {
2446
2494
  entries: entries.map((e) => ({
2447
2495
  filename: e.filename || "",
2448
2496
  content: e.content || "",
2449
- reason: e.reason || ""
2497
+ reason: e.reason || "",
2498
+ action: e.action === "update" ? "update" : "create"
2450
2499
  }))
2451
2500
  };
2452
2501
  }
@@ -2458,7 +2507,8 @@ function parseKnowledgeOutput(response) {
2458
2507
  entries: parsed.map((e) => ({
2459
2508
  filename: e.filename || "",
2460
2509
  content: e.content || "",
2461
- reason: e.reason || ""
2510
+ reason: e.reason || "",
2511
+ action: e.action === "update" ? "update" : "create"
2462
2512
  }))
2463
2513
  };
2464
2514
  }
@@ -5861,7 +5911,7 @@ async function uiKnowledgeCommand(entryName, options) {
5861
5911
  console.log(renderKnowledgeJSON(void 0, cwd));
5862
5912
  return;
5863
5913
  }
5864
- const { runKnowledgeInteractive: runKnowledgeInteractive2 } = await import("./knowledge-QC6464NS.js");
5914
+ const { runKnowledgeInteractive: runKnowledgeInteractive2 } = await import("./knowledge-TVXZATJX.js");
5865
5915
  await runKnowledgeInteractive2(cwd);
5866
5916
  }
5867
5917
 
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  renderKnowledgeJSON,
3
3
  runKnowledgeInteractive
4
- } from "./chunk-3QH66XVU.js";
4
+ } from "./chunk-OIFVTELS.js";
5
5
  import "./chunk-2MUV3F53.js";
6
6
  export {
7
7
  renderKnowledgeJSON,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spets",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Spec Driven Development Execution Framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -27,6 +27,12 @@
27
27
  - `pattern--` : 코드 패턴 (예: `pattern--error-handling`)
28
28
  - prefix 없이 사용해도 무방
29
29
 
30
+ ## Update or Create
31
+
32
+ - 기존 파일과 겹치는 내용이면 `"action": "update"`로 업데이트
33
+ - 새로운 지식이면 `"action": "create"`로 생성
34
+ - 중복 파일이 누적되지 않도록 기존 항목을 확인하세요
35
+
30
36
  ## 예시
31
37
 
32
38
  ```