@c4a/context 0.7.20 → 0.7.25

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.
@@ -217,7 +217,7 @@ In the work-start report or current plan, briefly state the affected articles'
217
217
  intended placements and reused category intent. For proposed top-level additions,
218
218
  renames, removals or changes of purpose, show the before/after tree, why reuse is
219
219
  insufficient, affected existing pages and reading order. Present that change for
220
- human review before applying it. This uses the existing report feedback where
220
+ human review under `context.gate.top_level_directory` before applying it. This uses the existing report feedback where
221
221
  available; do not add a new CLI state, schema field or routine per-article gate.
222
222
  Explicit user approval of that concrete structure is sufficient; do not ask again.
223
223
  General permission to write knowledge or organize batches is not approval to
@@ -587,6 +587,10 @@ before delivery; inspect both new content and the old articles' disposition.
587
587
 
588
588
  Preview approved-page retirement with `context task retire --input <file> --format json`:
589
589
 
590
+ This standalone retirement does not enter the article HTML Review. Apply
591
+ `context.gate.deletion_scope` to its target list; an already approved deletion
592
+ needs no second confirmation after the preview.
593
+
590
594
  ```yaml
591
595
  reason: These reader tasks are now covered by the approved guide.
592
596
  targets:
@@ -1,5 +1,9 @@
1
1
  # Prepare a workspace for the next task
2
2
 
3
+ Agent policy: `context.gate.workspace_reset_restore`. A clear request to
4
+ prepare this workspace authorizes the described task-state cleanup; clarify
5
+ only an unresolved target or loss.
6
+
3
7
  Use only for an explicit workspace preparation request. The goal is usable
4
8
  registered sources and no unfinished task, while retaining approved knowledge
5
9
  and configuration. Lead with Host tools and actual observations; do not start
@@ -1,5 +1,8 @@
1
1
  # Restore a historical workspace version
2
2
 
3
+ Agent policy: `context.gate.workspace_reset_restore`. An explicit restore
4
+ request needs no second approval when the target and affected files are clear.
5
+
3
6
  Use the Agent's Git and environment tools. This restores selected workspace
4
7
  files and usable sources; it does not reset the entire repository, rewind source
5
8
  repositories, or restore ignored runtime progress.
@@ -19,7 +22,8 @@ Compare target files with current tracked, untracked and staged files. Include
19
22
  current additions absent at the target in the proposed removal scope. Keep
20
23
  unrelated files, existing staged work and ignored source checkouts. Account for
21
24
  workspace moves or renames instead of treating a missing old path as an empty
22
- version. Reuse explicit authorization; ask when the target or loss is unresolved.
25
+ version. Reuse explicit authorization; clarify an unresolved target or loss
26
+ before acting rather than asking for a second approval of the same request.
23
27
 
24
28
  ## Restore files and environment
25
29
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c4a/context",
3
- "version": "0.7.20",
3
+ "version": "0.7.25",
4
4
  "type": "module",
5
5
  "description": "Declarative SDK for Context knowledge sources, workflows, review, and package outputs",
6
6
  "license": "MIT",
@@ -81,9 +81,10 @@ phrases, or several large indexes, run the bundled
81
81
  node <current knowledge-query Skill directory>/scripts/search.mjs --query '<terms>' --limit 8
82
82
  ```
83
83
 
84
- The script locates `{{packageName}}` when it runs inside the package tree. If a
85
- package manager copied this Skill elsewhere, add `--root <package directory
86
- containing context-build-inventory.json>`, or use `--base <package collection>`
84
+ The script locates `{{packageName}}` when it runs inside the package tree. It accepts
85
+ the build inventory at the package root or its distributed copy under
86
+ `others/context/`. If a package manager copied this Skill elsewhere, add
87
+ `--root <package directory>`, or use `--base <package collection>`
87
88
  to locate this package by its inventory name. It chunks Markdown mechanically
88
89
  by headings and bounded line ranges, then returns paths, line ranges, headings,
89
90
  and previews; it does not interpret meaning.
@@ -138,7 +139,8 @@ explicitly accept the unchanged default during Context package-template review.
138
139
 
139
140
  After answering from the pages actually read, optionally append one or more
140
141
  related document links in the final summary. Read `context-site-map.json` from
141
- the selected package (or its sibling website output in a workspace). Match the
142
+ the selected package root, then `others/context/context-site-map.json`, or its
143
+ sibling website output in a workspace. Match the
142
144
  read page to `pages[].package_path` or `approved_path`; use its `title` and resolve
143
145
  `site_path` relative to `site_url`. The URL already includes the deployment base
144
146
  path: do not prepend `base` again. Only cite matched articles, deduplicate links,
@@ -51,7 +51,14 @@ function parseArgs(argv) {
51
51
  }
52
52
 
53
53
  function isPackageRoot(directory) {
54
- return existsSync(join(directory, "context-build-inventory.json"));
54
+ return inventoryPath(directory) !== undefined;
55
+ }
56
+
57
+ function inventoryPath(directory) {
58
+ return [
59
+ join(directory, "context-build-inventory.json"),
60
+ join(directory, "others", "context", "context-build-inventory.json"),
61
+ ].find((path) => existsSync(path));
55
62
  }
56
63
 
57
64
  function ancestorPackageRoot(start) {
@@ -66,8 +73,8 @@ function ancestorPackageRoot(start) {
66
73
 
67
74
  function findInventories(directory, depth = 0) {
68
75
  if (!existsSync(directory) || depth > 5) return [];
69
- const inventory = join(directory, "context-build-inventory.json");
70
- if (existsSync(inventory)) return [inventory];
76
+ const inventory = inventoryPath(directory);
77
+ if (inventory !== undefined) return [inventory];
71
78
  const matches = [];
72
79
  for (const entry of readdirSync(directory, { withFileTypes: true })) {
73
80
  if (!entry.isDirectory()) continue;
@@ -76,6 +83,12 @@ function findInventories(directory, depth = 0) {
76
83
  return matches;
77
84
  }
78
85
 
86
+ function packageRootFromInventory(inventory) {
87
+ return inventory.endsWith(join("others", "context", "context-build-inventory.json"))
88
+ ? dirname(dirname(dirname(inventory)))
89
+ : dirname(inventory);
90
+ }
91
+
79
92
  function inventoryPackageName(inventoryPath) {
80
93
  try {
81
94
  return JSON.parse(readFileSync(inventoryPath, "utf8"))?.package?.name;
@@ -99,7 +112,7 @@ function resolvePackageRoot(explicitRoot, packageCollection) {
99
112
  }
100
113
  const inventories = findInventories(resolve(packageCollection));
101
114
  const matching = inventories.filter((path) => inventoryPackageName(path) === PACKAGE_NAME);
102
- if (matching.length === 1) return dirname(matching[0]);
115
+ if (matching.length === 1) return packageRootFromInventory(matching[0]);
103
116
  if (matching.length > 1) {
104
117
  throw new Error(`multiple installed packages named ${PACKAGE_NAME}; pass --root explicitly`);
105
118
  }
@@ -107,10 +120,10 @@ function resolvePackageRoot(explicitRoot, packageCollection) {
107
120
  }
108
121
 
109
122
  function knowledgeRoots(packageRoot) {
110
- const inventoryPath = join(packageRoot, "context-build-inventory.json");
123
+ const packageInventoryPath = inventoryPath(packageRoot);
111
124
  let declared = [];
112
125
  try {
113
- const inventory = JSON.parse(readFileSync(inventoryPath, "utf8"));
126
+ const inventory = JSON.parse(readFileSync(packageInventoryPath, "utf8"));
114
127
  declared = Object.values(inventory?.package?.distribution?.roots ?? {})
115
128
  .filter((value) => typeof value === "string");
116
129
  } catch {
@@ -60,7 +60,7 @@ description: 查询 {{displayName}} 中经过审核、可追溯来源的知识
60
60
  node <当前 knowledge-query Skill 目录>/scripts/search.mjs --query '<关键词>' --limit 8
61
61
  ```
62
62
 
63
- 脚本位于知识包目录内时会自动定位 `{{packageName}}`。如果包管理工具将本 Skill 复制到了其他位置,增加 `--root <包含 context-build-inventory.json 的包目录>`;也可以使用 `--base <多包集合根目录>`,按构建清单中的包名定位。它按 Markdown 标题和固定行块机械切分,返回路径、行号、标题和短预览,不判断内容语义。
63
+ 脚本位于知识包目录内时会自动定位 `{{packageName}}`。它兼容包根的构建清单及 `others/context/` 下的分发副本。如果包管理工具将本 Skill 复制到了其他位置,增加 `--root <知识包目录>`;也可以使用 `--base <多包集合根目录>`,按构建清单中的包名定位。它按 Markdown 标题和固定行块机械切分,返回路径、行号、标题和短预览,不判断内容语义。
64
64
 
65
65
  搜索命中只是线索。回答前打开命中的页面和章节;关系或影响范围须引用实际说明该关系的正文,不能用 BM25 分数或文本共现代替。
66
66
 
@@ -101,7 +101,7 @@ node <当前 knowledge-query Skill 目录>/scripts/search.mjs --query '<关键
101
101
  ## 总结中的网站文档链接
102
102
 
103
103
  完成正文检索和回答后,仅在最终总结中按需附上一篇或多篇实际读取过的相关文档链接。
104
- 从当前知识包读取 `context-site-map.json`,按命中文章的包内路径匹配
104
+ 依次从当前知识包根、`others/context/context-site-map.json` 或工作区网站输出读取站点映射,按命中文章的包内路径匹配
105
105
  `pages[].package_path`,使用 `title` 作链接文字,将 `site_path` 相对
106
106
  `site_url` 拼接。`site_url` 已包含站点子路径,不再重复拼接 `base`。
107
107
  同一文章只列一次,不凭标题推测页面地址或章节锚点。
@@ -51,7 +51,14 @@ function parseArgs(argv) {
51
51
  }
52
52
 
53
53
  function isPackageRoot(directory) {
54
- return existsSync(join(directory, "context-build-inventory.json"));
54
+ return inventoryPath(directory) !== undefined;
55
+ }
56
+
57
+ function inventoryPath(directory) {
58
+ return [
59
+ join(directory, "context-build-inventory.json"),
60
+ join(directory, "others", "context", "context-build-inventory.json"),
61
+ ].find((path) => existsSync(path));
55
62
  }
56
63
 
57
64
  function ancestorPackageRoot(start) {
@@ -66,8 +73,8 @@ function ancestorPackageRoot(start) {
66
73
 
67
74
  function findInventories(directory, depth = 0) {
68
75
  if (!existsSync(directory) || depth > 5) return [];
69
- const inventory = join(directory, "context-build-inventory.json");
70
- if (existsSync(inventory)) return [inventory];
76
+ const inventory = inventoryPath(directory);
77
+ if (inventory !== undefined) return [inventory];
71
78
  const matches = [];
72
79
  for (const entry of readdirSync(directory, { withFileTypes: true })) {
73
80
  if (!entry.isDirectory()) continue;
@@ -76,6 +83,12 @@ function findInventories(directory, depth = 0) {
76
83
  return matches;
77
84
  }
78
85
 
86
+ function packageRootFromInventory(inventory) {
87
+ return inventory.endsWith(join("others", "context", "context-build-inventory.json"))
88
+ ? dirname(dirname(dirname(inventory)))
89
+ : dirname(inventory);
90
+ }
91
+
79
92
  function inventoryPackageName(inventoryPath) {
80
93
  try {
81
94
  return JSON.parse(readFileSync(inventoryPath, "utf8"))?.package?.name;
@@ -99,7 +112,7 @@ function resolvePackageRoot(explicitRoot, packageCollection) {
99
112
  }
100
113
  const inventories = findInventories(resolve(packageCollection));
101
114
  const matching = inventories.filter((path) => inventoryPackageName(path) === PACKAGE_NAME);
102
- if (matching.length === 1) return dirname(matching[0]);
115
+ if (matching.length === 1) return packageRootFromInventory(matching[0]);
103
116
  if (matching.length > 1) {
104
117
  throw new Error(`multiple installed packages named ${PACKAGE_NAME}; pass --root explicitly`);
105
118
  }
@@ -107,10 +120,10 @@ function resolvePackageRoot(explicitRoot, packageCollection) {
107
120
  }
108
121
 
109
122
  function knowledgeRoots(packageRoot) {
110
- const inventoryPath = join(packageRoot, "context-build-inventory.json");
123
+ const packageInventoryPath = inventoryPath(packageRoot);
111
124
  let declared = [];
112
125
  try {
113
- const inventory = JSON.parse(readFileSync(inventoryPath, "utf8"));
126
+ const inventory = JSON.parse(readFileSync(packageInventoryPath, "utf8"));
114
127
  declared = Object.values(inventory?.package?.distribution?.roots ?? {})
115
128
  .filter((value) => typeof value === "string");
116
129
  } catch {