@guanlin4924/devflow-pi 0.1.77 → 0.1.79

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guanlin4924/devflow-pi",
3
- "version": "0.1.77",
3
+ "version": "0.1.79",
4
4
  "type": "module",
5
5
  "description": "Devflow 专项研发 Pi,基于固定版本 OMP Runtime 组装研发状态机、代码智能、设计、实现与质量门禁。",
6
6
  "license": "UNLICENSED",
@@ -20,7 +20,14 @@
20
20
  "bun": ">=1.3.14"
21
21
  },
22
22
  "dependencies": {
23
- "@oh-my-pi/pi-coding-agent": "17.2.7"
23
+ "@oh-my-pi/pi-coding-agent": "17.2.7",
24
+ "@plurnk/plurnk-mimetypes-grammar-cpp": "1.16.1",
25
+ "@plurnk/plurnk-mimetypes-grammar-kotlin": "1.16.1",
26
+ "@plurnk/plurnk-mimetypes-grammar-ruby": "1.16.1",
27
+ "@plurnk/plurnk-mimetypes-grammar-scala": "1.16.1",
28
+ "@plurnk/plurnk-mimetypes-grammar-yaml": "1.16.1",
29
+ "tree-sitter-graphql-grammar-wasm": "1.1.0",
30
+ "web-tree-sitter": "0.25.10"
24
31
  },
25
32
  "devDependencies": {
26
33
  "@types/bun": "^1.3.14",
@@ -53,9 +53,11 @@ export default function devflowIntel(pi: ExtensionAPI) {
53
53
  label: "Devflow Code Index",
54
54
  description: "Scan, validate, audit, or query the deterministic project code index.",
55
55
  parameters: z.object({
56
- action: z.enum(["scan", "validate", "audit", "query", "relations"]),
56
+ action: z.enum(["scan", "validate", "audit", "query", "relations", "impact"]),
57
57
  term: z.string().optional(),
58
58
  object: z.string().optional(),
59
+ entity: z.string().optional(),
60
+ changeType: z.enum(["remove_field", "rename_field", "change_type", "optional_to_required", "required_to_optional", "change_nullability", "change_enum"]).optional(),
59
61
  snapshot: z.string().optional(),
60
62
  depth: z.number().int().min(0).max(20).default(3),
61
63
  includeHeuristic: z.boolean().default(false),
@@ -76,8 +78,10 @@ export default function devflowIntel(pi: ExtensionAPI) {
76
78
  if (params.changedFiles.length > 0) args.push("--changed-files", ...params.changedFiles);
77
79
  } else if (params.action === "validate") {
78
80
  args.push("--index", ".planning/code-index");
81
+ if (params.snapshot) args.push("--snapshot", params.snapshot);
79
82
  } else if (params.action === "audit") {
80
83
  args.push("--root", ctx.cwd, "--index", ".planning/code-index", "--skill-root", skillRoot);
84
+ if (params.snapshot) args.push("--snapshot", params.snapshot);
81
85
  } else if (params.action === "query") {
82
86
  if (!params.term) {
83
87
  return render({ ok: false, exitCode: 2, stdout: "", stderr: "term is required" }, {});
@@ -85,6 +89,13 @@ export default function devflowIntel(pi: ExtensionAPI) {
85
89
  args.push("--index", ".planning/code-index", "--term", params.term, "--limit", String(params.limit));
86
90
  if (params.snapshot) args.push("--snapshot", params.snapshot);
87
91
  } else {
92
+ if (params.action === "impact") {
93
+ if (!params.entity) return render({ ok: false, exitCode: 2, stdout: "", stderr: "entity is required" }, {});
94
+ args.push("--index", ".planning/code-index", "--entity", params.entity, "--depth", String(params.depth), "--limit", String(params.limit));
95
+ if (params.snapshot) args.push("--snapshot", params.snapshot);
96
+ if (params.changeType) args.push("--change-type", params.changeType);
97
+ return render(await run(args, ctx.cwd), { action: params.action });
98
+ }
88
99
  if (!params.object) return render({ ok: false, exitCode: 2, stdout: "", stderr: "object is required" }, {});
89
100
  args.push("--index", ".planning/code-index", "--object", params.object, "--depth", String(params.depth), "--limit", String(params.limit));
90
101
  if (params.snapshot) args.push("--snapshot", params.snapshot);
@@ -32,6 +32,14 @@ devflow_code_index(action="scan")
32
32
  devflow_code_index(action="audit")
33
33
  ```
34
34
 
35
+ Impact analysis uses the same immutable SQLite snapshot:
36
+
37
+ ```text
38
+ devflow_code_index(action="impact", entity="{entity_id}", changeType="remove_field")
39
+ ```
40
+
41
+ The result carries `_meta.snapshot`, coverage, and truncation status. Older structural-only snapshots return an explicit semantic capability-unavailable result; they remain valid for symbol and relation queries.
42
+
35
43
  ## Query Patterns
36
44
 
37
45
  For more detailed query patterns, read `references/query-patterns.md`.
@@ -44,6 +44,7 @@ Use the OMP runtime tool:
44
44
  devflow_code_index(action="scan")
45
45
  devflow_code_index(action="validate")
46
46
  devflow_code_index(action="audit")
47
+ devflow_code_index(action="impact", entity="api:orders", changeType="remove_field")
47
48
  ```
48
49
 
49
50
  For changed-file indexing:
@@ -66,3 +67,7 @@ Blocking findings are stale file hashes, missing indexed files, dangling `to_id`
66
67
  - Do not mark dynamic or string-based matches as `exact`.
67
68
  - If adapter support is missing, emit diagnostics and fall back to generic indexing.
68
69
  - Query answers must read source files at evidence paths before making factual claims.
70
+ - The root `SNAPSHOT.json` is only a pointer. Queries, validation, audit, and impact resolve it to the immutable `snapshots/{generation}` directory before reading facts.
71
+ - Query results include snapshot and parser provenance. A historical snapshot may be queried with `snapshot`; it is not freshness-checked against the current worktree.
72
+ - `impact` is a typed semantic query. It returns `unavailable` with `semantic_capability_unavailable` when an older structural-only snapshot has no semantic tables; it never executes arbitrary SQL.
73
+ - Query limits are bounded by the runtime. Negative or oversized limits are normalized safely.
@@ -18,6 +18,9 @@ The result contains:
18
18
  - `symbols`
19
19
  - `relations`
20
20
  - `diagnostics`
21
+ - `actual_parser` — parser that produced the returned facts; required when a
22
+ selected native adapter falls back after a dependency or per-file parse
23
+ failure
21
24
 
22
25
  ## Resolution Rules
23
26
 
@@ -36,3 +39,8 @@ The result contains:
36
39
 
37
40
  Core schema and writers must not change when a new adapter is added.
38
41
 
42
+ Declared and runtime parser capabilities are recorded in
43
+ `scripts/adapters/parser-capabilities.json` and the snapshot capability map.
44
+ An unavailable native or Tree-sitter parser must report its fallback and emit
45
+ an `adapter_missing` diagnostic; structured lexical output must never be
46
+ labelled as native AST output.
@@ -34,6 +34,7 @@ All adapters must emit these records. Examples use placeholders only; do not rep
34
34
  "language": "{language_id}",
35
35
  "content_hash": "sha256:{hash}",
36
36
  "visibility": "{optional_visibility}",
37
+ "parser": "{adapter}@{version}",
37
38
  "metadata": {}
38
39
  }
39
40
  ```
@@ -71,4 +72,3 @@ Symbol IDs must not depend on line numbers. Use language, codebase, stable quali
71
72
  "parser": "{adapter}@{version}"
72
73
  }
73
74
  ```
74
-
@@ -0,0 +1,26 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import Dict
4
+
5
+
6
+ MANIFEST_PATH = Path(__file__).resolve().parent / "parser-capabilities.json"
7
+
8
+
9
+ def load_capability_manifest() -> Dict:
10
+ manifest = json.loads(MANIFEST_PATH.read_text(encoding="utf-8"))
11
+ if manifest.get("schemaVersion") != 1 or not isinstance(manifest.get("languages"), dict):
12
+ raise ValueError("invalid parser capability manifest")
13
+ return manifest
14
+
15
+
16
+ def confidence_for_parser(parser: str, kind: str = "symbol") -> str:
17
+ """Map extractor provenance to the strongest defensible confidence."""
18
+ if kind == "file":
19
+ return "exact"
20
+ if parser.startswith(("python-ast@", "typescript-compiler-ast@", "go-parser-ast@", "javac-tree-ast@", "nikic-php-parser-ast@")):
21
+ return "exact"
22
+ if parser.startswith("tree-sitter-"):
23
+ return "exact"
24
+ if parser.startswith("structured-scope@"):
25
+ return "inferred"
26
+ return "heuristic"
@@ -95,8 +95,9 @@ class GenericTextAdapter(Adapter):
95
95
  path,
96
96
  symbol["line_start"],
97
97
  self.parser_id,
98
- confidence="exact",
98
+ confidence="heuristic",
99
99
  to_id=symbol["id"],
100
+ metadata={"extraction": "text_pattern"},
100
101
  )
101
102
  )
102
103
 
@@ -289,6 +290,7 @@ def _symbol(codebase, language, kind, name, path, line_start, line_end, content_
289
290
  "language": language,
290
291
  "content_hash": content_hash,
291
292
  "visibility": visibility,
293
+ "parser": parser,
292
294
  "metadata": {"parser": parser} if parser else {},
293
295
  }
294
296
 
@@ -0,0 +1,50 @@
1
+ // Optional helper for web-tree-sitter. It is intentionally dependency-free at
2
+ // package install time; the runtime reports parser_unavailable when the module
3
+ // or grammar is absent.
4
+ import fs from "node:fs";
5
+ import process from "node:process";
6
+
7
+ const grammarPath = process.argv[2];
8
+ const input = JSON.parse(fs.readFileSync(0, "utf8").trim());
9
+ try {
10
+ const mod = await import("web-tree-sitter");
11
+ const Parser = mod.default ?? mod.Parser;
12
+ const Language = mod.Language;
13
+ await Parser.init();
14
+ const language = await Language.load(grammarPath);
15
+ const parser = new Parser();
16
+ parser.setLanguage(language);
17
+ const tree = parser.parse(input.source);
18
+ const declarations = [];
19
+ const calls = [];
20
+ const declarationTypes = /(function|method|class|struct|interface|trait|enum|module|namespace|impl|object|type)/i;
21
+ const named = (node, field) => {
22
+ const child = node.childForFieldName?.(field);
23
+ return child ? child.text : "";
24
+ };
25
+ const walk = (node, parent = "") => {
26
+ const type = String(node.type || "");
27
+ const key = `${node.startIndex}:${node.endIndex}:${type}`;
28
+ if (declarationTypes.test(type) && !/(identifier|name|parameter)/i.test(type)) {
29
+ const name = named(node, "name") || named(node, "declarator") || `${type}@${node.startPosition.row + 1}`;
30
+ declarations.push({key, parent, kind: type, name, line_start: node.startPosition.row + 1, line_end: node.endPosition.row + 1});
31
+ parent = key;
32
+ }
33
+ if (/call|invocation|command_substitution/i.test(type)) {
34
+ const fn = named(node, "function") || named(node, "name") || node.firstNamedChild?.text || type;
35
+ calls.push({owner: parent, target: fn, line: node.startPosition.row + 1});
36
+ }
37
+ for (const child of node.namedChildren ?? []) walk(child, parent);
38
+ };
39
+ walk(tree.rootNode);
40
+ // web-tree-sitter exposes hasError as a boolean property (older releases
41
+ // exposed a method), so support both without treating a valid parse as a
42
+ // helper failure.
43
+ const hasErrorValue = tree.rootNode.hasError;
44
+ const hasError = typeof hasErrorValue === "function" ? hasErrorValue.call(tree.rootNode) : Boolean(hasErrorValue);
45
+ console.log(JSON.stringify({ok: true, parse_status: hasError ? "partial" : "complete", declarations, calls,
46
+ diagnostics: [], metadata: {grammar: grammarPath, error_tree: hasError}}));
47
+ } catch (error) {
48
+ console.log(JSON.stringify({ok: false, error: String(error?.message || error)}));
49
+ process.exitCode = 2;
50
+ }