@guanlin4924/devflow-pi 0.1.75 → 0.1.76
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 +1 -1
- package/plugins/devflow-core/extensions/devflow-core.ts +1 -1
- package/plugins/devflow-core/skills/workflow-project-context/scripts/validate_initialization_database.py +2 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/semantic.py +10 -5
- package/src/constants.ts +1 -1
- package/src/initialization-store.ts +11 -0
package/package.json
CHANGED
|
@@ -1318,7 +1318,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1318
1318
|
value = await store.registerWorkspace({ rebindWorkspaceId: params.workspaceId });
|
|
1319
1319
|
} else if (params.action === "cancel") {
|
|
1320
1320
|
if (!params.runId) throw new Error("runId is required for cancel");
|
|
1321
|
-
value = { runId: params.runId, cancelled: store.
|
|
1321
|
+
value = { runId: params.runId, cancelled: store.cancelRunByOperator(params.runId) };
|
|
1322
1322
|
} else if (params.action === "recover") {
|
|
1323
1323
|
if (!params.runId) throw new Error("runId is required for recover");
|
|
1324
1324
|
value = { runId: params.runId, ...store.recoverRun(params.runId) };
|
|
@@ -61,6 +61,8 @@ def validate(database: Path, project_id: str, workspace_id: str | None = None, r
|
|
|
61
61
|
for run in runs[:1] if not run_id else runs:
|
|
62
62
|
if run["status"] not in TERMINAL_STATUSES | {"preparing", "collecting", "validating"}:
|
|
63
63
|
errors.append(f"invalid run status: {run['status']}")
|
|
64
|
+
if run["status"] != "finished":
|
|
65
|
+
errors.append(f"initialization run is not finished: {run['status']}")
|
|
64
66
|
results = connection.execute(
|
|
65
67
|
"SELECT repository,kind,status FROM collection_results WHERE run_id=?", (run["id"],)
|
|
66
68
|
).fetchall()
|
|
@@ -19,11 +19,14 @@ def build_semantic_facts(root: Path, files: Iterable[dict], symbols: Iterable[di
|
|
|
19
19
|
entities: List[dict] = []
|
|
20
20
|
flows: List[dict] = []
|
|
21
21
|
bindings: List[dict] = []
|
|
22
|
+
records = list(files)
|
|
22
23
|
symbol_by_path = {}
|
|
23
24
|
for symbol in symbols:
|
|
24
25
|
symbol_by_path.setdefault(symbol.get("path"), []).append(symbol)
|
|
25
26
|
|
|
26
|
-
for file_record in
|
|
27
|
+
for file_number, file_record in enumerate(records, 1):
|
|
28
|
+
if file_number == 1 or file_number % 250 == 0 or file_number == len(records):
|
|
29
|
+
print(f"[code-index][semantic] processed {file_number}/{len(records)} files", flush=True)
|
|
27
30
|
path = file_record["path"]
|
|
28
31
|
source = _read(root, path)
|
|
29
32
|
if source is None:
|
|
@@ -45,10 +48,11 @@ def build_semantic_facts(root: Path, files: Iterable[dict], symbols: Iterable[di
|
|
|
45
48
|
if symbol.get("kind") in {"route", "component", "class", "function", "method"}:
|
|
46
49
|
bindings.append(_binding(feature_id, symbol["id"], "static_inferred", "heuristic", path, symbol.get("line_start", 1)))
|
|
47
50
|
|
|
51
|
+
state_ids = set()
|
|
48
52
|
for line_no, line in enumerate(source.splitlines(), 1):
|
|
49
53
|
_extract_api(line, path, file_record, line_no, entities, flows)
|
|
50
54
|
_extract_db(line, path, file_record, line_no, entities, flows)
|
|
51
|
-
_extract_ui(line, path, file_record, line_no, entities, flows)
|
|
55
|
+
_extract_ui(line, path, file_record, line_no, entities, flows, state_ids)
|
|
52
56
|
|
|
53
57
|
# Existing structural relations remain the source of method call edges;
|
|
54
58
|
# expose them in the semantic graph without changing their meaning.
|
|
@@ -110,7 +114,8 @@ def _extract_db(line, path, record, line_no, entities, flows):
|
|
|
110
114
|
flows.append(_flow(table_id, column_id, f"{operation}_column", path, line_no, "heuristic", "sql_column_regex"))
|
|
111
115
|
|
|
112
116
|
|
|
113
|
-
def _extract_ui(line, path, record, line_no, entities, flows):
|
|
117
|
+
def _extract_ui(line, path, record, line_no, entities, flows, state_ids=None):
|
|
118
|
+
state_ids = state_ids if state_ids is not None else set()
|
|
114
119
|
component = re.search(r"\b(?:function|class)\s+([A-Z][A-Za-z0-9_]*)|<([A-Z][A-Za-z0-9_.]*)\b", line)
|
|
115
120
|
state = re.search(r"\b(?:useState|ref|reactive|computed|createSignal)\s*\(.*?\)|\b(?:const|let|var)\s+([A-Za-z_][\w]*(?:State|Store|Status|Loading|Error))\s*=", line)
|
|
116
121
|
condition = re.search(r"\b(?:v-if|v-show)\s*=\s*['\"]([^'\"]+)['\"]|(?:\{\s*)?([^{}\n]+?)\s*&&\s*<", line)
|
|
@@ -125,16 +130,16 @@ def _extract_ui(line, path, record, line_no, entities, flows):
|
|
|
125
130
|
name = state.group(1) or "anonymous_state"
|
|
126
131
|
state_id = f"ui-state:{stable_key(path)}:{stable_key(name)}"
|
|
127
132
|
entities.append(_entity(state_id, "ui_state", name, record, line_no, line_no, "heuristic", "state_pattern"))
|
|
133
|
+
state_ids.add(state_id)
|
|
128
134
|
flows.append(_flow(component_id, state_id, "state_reads", path, line_no, "heuristic", "state_pattern"))
|
|
129
135
|
if condition:
|
|
130
136
|
expression = condition.group(1) or condition.group(2)
|
|
131
137
|
cond_id = f"render-condition:{stable_key(path)}:{short_hash(expression)}"
|
|
132
138
|
entities.append(_entity(cond_id, "render_condition", expression, record, line_no, line_no, "heuristic", "template_condition"))
|
|
133
139
|
flows.append(_flow(component_id, cond_id, "renders_when", path, line_no, "heuristic", "template_condition"))
|
|
134
|
-
known_ids = {item["id"] for item in entities if item.get("entity_type") == "ui_state"}
|
|
135
140
|
for name in re.findall(r"\b[A-Za-z_][A-Za-z0-9_]*\b", expression):
|
|
136
141
|
state_id = f"ui-state:{stable_key(path)}:{stable_key(name)}"
|
|
137
|
-
if state_id in
|
|
142
|
+
if state_id in state_ids:
|
|
138
143
|
flows.append(_flow(cond_id, state_id, "depends_on", path, line_no, "heuristic", "condition_identifier"))
|
|
139
144
|
|
|
140
145
|
|
package/src/constants.ts
CHANGED
|
@@ -455,6 +455,17 @@ export class InitializationStore {
|
|
|
455
455
|
return this.db.query("UPDATE initialization_runs SET status='cancelled',updated_at=? WHERE id=? AND lease_token=? AND lease_expires_at>? AND status IN ('preparing','collecting','validating')").run(timestamp, runId, leaseToken, timestamp).changes === 1;
|
|
456
456
|
}
|
|
457
457
|
|
|
458
|
+
/** Operator cancellation invalidates the worker lease so a detached worker cannot publish later. */
|
|
459
|
+
cancelRunByOperator(runId: string, reason = "cancelled by operator"): boolean {
|
|
460
|
+
const replacementLease = randomUUID();
|
|
461
|
+
const result = this.db.query(`UPDATE initialization_runs
|
|
462
|
+
SET status='cancelled', lease_token=?, error=?, updated_at=?
|
|
463
|
+
WHERE id=? AND status IN ('preparing','collecting','validating')`).run(
|
|
464
|
+
replacementLease, reason.slice(0, 4000), now(), runId,
|
|
465
|
+
);
|
|
466
|
+
return result.changes === 1;
|
|
467
|
+
}
|
|
468
|
+
|
|
458
469
|
failRun(runId: string, leaseToken: string, error: string): boolean {
|
|
459
470
|
this.assertWritableRun(runId, leaseToken);
|
|
460
471
|
const timestamp = now();
|