@ngockhoale/ukit 3.4.29 → 3.4.30
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/CHANGELOG.md +16 -0
- package/manifests/documentation.yaml +88 -0
- package/package.json +1 -1
- package/src/core/permissionPolicy.js +1 -1
- package/src/index/buildIndex.js +244 -4
- package/src/index/impactContext.js +91 -11
- package/src/index/languageTools.js +126 -0
- package/src/index/paths.js +5 -1
- package/src/index/playbookRegistry.js +108 -9
- package/src/index/queryIndex.js +415 -1
- package/src/index/routeCatalog.js +1 -1
- package/src/index/taskRouting.js +25 -3
- package/template_project/.claude/commands/ukit/handoff-create.md +5 -0
- package/template_project/.claude/commands/ukit/handoff-fullstack.md +43 -4
- package/template_project/.claude/hooks/block-dangerous.mjs +7 -1
- package/template_project/.claude/hooks/handoff-model-guard.sh +235 -21
- package/template_project/.claude/hooks/record-execution.mjs +23 -0
- package/template_project/.claude/hooks/skill-router.sh +105 -7
- package/template_project/.claude/settings.json +21 -1
- package/template_project/.claude/ukit/index/cleanup-task-worktree.mjs +1 -1
- package/template_project/.claude/ukit/index/impact-context.mjs +70 -15
- package/template_project/.claude/ukit/index/land-worktree.mjs +2 -2
- package/template_project/.claude/ukit/index/lib/index-core.mjs +883 -14
- package/template_project/.claude/ukit/index/playbook-registry.mjs +108 -9
- package/template_project/.claude/ukit/index/query-index.mjs +100 -7
- package/template_project/.claude/ukit/index/route-catalog.mjs +1 -1
- package/template_project/.claude/ukit/index/route-task.mjs +208 -31
- package/template_project/.claude/ukit/runtime/execution-ledger.mjs +453 -24
- package/template_project/.claude/ukit/runtime/handoff-intent.mjs +122 -6
- package/template_project/.claude/ukit/runtime/resumable-run.mjs +417 -0
- package/template_project/.claude/ukit/runtime/stop-coordinator.mjs +635 -28
- package/template_project/.omp/hooks/pre/ukit-bridge.js +32 -4
- package/template_user/playbooks/feature-implementation.md +9 -0
- package/template_user/playbooks/migration.md +4 -0
|
@@ -167,7 +167,7 @@ const APPROVAL_KEYS = [
|
|
|
167
167
|
'schemaVersion', 'taskId', 'receiptSha256', 'verdict', 'executorModel',
|
|
168
168
|
'reviewerModel', 'reviewedResultTree',
|
|
169
169
|
];
|
|
170
|
-
const TASK_TOKEN = /^TASK-
|
|
170
|
+
const TASK_TOKEN = /^TASK-(?:[A-Z]\d{1,3}-)?\d{3,4}$/;
|
|
171
171
|
const OID = /^[0-9a-f]{40}$/;
|
|
172
172
|
|
|
173
173
|
function readJson(p, taskId, label) {
|
|
@@ -34,30 +34,85 @@ async function main() {
|
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
37
|
+
let impact = null;
|
|
38
|
+
try {
|
|
39
|
+
await ensureFreshIndex(rootDir);
|
|
40
|
+
impact = resolveImpactContext
|
|
41
|
+
? await resolveImpactContext({ rootDir, changedFiles, changedSymbols })
|
|
42
|
+
: {
|
|
43
|
+
changedFiles,
|
|
44
|
+
changedSymbols,
|
|
45
|
+
riskLabels: [],
|
|
46
|
+
callees: [],
|
|
47
|
+
callers: [],
|
|
48
|
+
importers: [],
|
|
49
|
+
mirrorCounterparts: [],
|
|
50
|
+
relatedTests: [],
|
|
51
|
+
recommendedTestFiles: [],
|
|
52
|
+
};
|
|
53
|
+
} catch (error) {
|
|
54
|
+
// Index missing, rebuild failed, or lock timed out — answer from source
|
|
55
|
+
// with a bounded search instead of exiting 1.
|
|
56
|
+
const answered = await runFallbackSearch(rootDir, changedFiles, changedSymbols);
|
|
57
|
+
if (!answered) {
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
52
62
|
|
|
53
63
|
printCompact(impact);
|
|
54
64
|
|
|
65
|
+
if ((impact.unverifiedImpact ?? []).length > 0) {
|
|
66
|
+
console.log(`unverified-impact: ${impact.unverifiedImpact.slice(0, CALLER_CAP).join(', ')}`);
|
|
67
|
+
console.log('note: name-only matches without import proof — verify before acting');
|
|
68
|
+
}
|
|
69
|
+
const truncated = impact.truncated ?? {};
|
|
70
|
+
const truncatedParts = Object.entries(truncated).filter(([, count]) => count > 0).map(([key, count]) => `${key}:+${count}`);
|
|
71
|
+
if (truncatedParts.length > 0) {
|
|
72
|
+
console.log(`truncated: ${truncatedParts.join(', ')}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
55
75
|
if (formatImpactConfidenceSummary) {
|
|
56
76
|
console.log('');
|
|
57
77
|
console.log(formatImpactConfidenceSummary(impact));
|
|
58
78
|
}
|
|
59
79
|
}
|
|
60
80
|
|
|
81
|
+
// Bounded source search over the changed file basenames and symbol names.
|
|
82
|
+
// Returns true when it produced output; false when the fallback itself is
|
|
83
|
+
// unavailable so the original index error can surface.
|
|
84
|
+
async function runFallbackSearch(rootDir, changedFiles, changedSymbols) {
|
|
85
|
+
if (typeof indexCore.boundedSourceSearch !== 'function') {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
const queries = [
|
|
89
|
+
...changedFiles.map((filePath) => String(filePath).split('/').pop().replace(/\.[^.]+$/, '')),
|
|
90
|
+
...changedSymbols,
|
|
91
|
+
].filter(Boolean).slice(0, 4);
|
|
92
|
+
try {
|
|
93
|
+
console.log('source: fallback-search');
|
|
94
|
+
let printed = 0;
|
|
95
|
+
for (const searchQuery of queries) {
|
|
96
|
+
const rows = await indexCore.boundedSourceSearch({ rootDir, query: searchQuery, limit: 5 });
|
|
97
|
+
console.log(`[ukit:impact:fallback] ${rows.length} source matches for: ${searchQuery}`);
|
|
98
|
+
for (const row of rows) {
|
|
99
|
+
console.log(` ${row.filePath}${row.line ? `:${row.line}` : ''}`);
|
|
100
|
+
}
|
|
101
|
+
if (rows.truncated?.length) {
|
|
102
|
+
console.log(`[ukit:impact:fallback] search truncated (${rows.truncated.join(',')}) — NOT exhaustive`);
|
|
103
|
+
}
|
|
104
|
+
printed += rows.length;
|
|
105
|
+
}
|
|
106
|
+
if (printed === 0) {
|
|
107
|
+
console.log('[ukit:impact:fallback] no source matches — verify changed files manually');
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
} catch (fallbackError) {
|
|
111
|
+
console.error(`fallback-search failed: ${fallbackError && fallbackError.message ? fallbackError.message : fallbackError}`);
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
61
116
|
async function ensureFreshIndex(rootDir) {
|
|
62
117
|
if (typeof indexCore.ensureIndexFresh === 'function') {
|
|
63
118
|
return indexCore.ensureIndexFresh({ rootDir });
|
|
@@ -168,8 +168,8 @@ function readScope() {
|
|
|
168
168
|
}
|
|
169
169
|
if (parsed.schemaVersion !== 1) refuse(parsed.taskId, 'invalid-scope', 'schemaVersion must be 1');
|
|
170
170
|
const { taskId, baseCommit, sourceRoot, destinationRoot, sourceHead, destinationHead, targetPaths } = parsed;
|
|
171
|
-
if (typeof taskId !== 'string' || !/^TASK-
|
|
172
|
-
refuse(taskId, 'invalid-scope', 'taskId must be an exact TASK-C97-009
|
|
171
|
+
if (typeof taskId !== 'string' || !/^TASK-(?:[A-Z]\d{1,3}-)?\d{3,4}$/.test(taskId)) {
|
|
172
|
+
refuse(taskId, 'invalid-scope', 'taskId must be an exact TASK-001 or TASK-C97-009 style token');
|
|
173
173
|
}
|
|
174
174
|
for (const [k, v] of Object.entries({ baseCommit, sourceRoot, destinationRoot, sourceHead, destinationHead })) {
|
|
175
175
|
if (typeof v !== 'string' || v.length === 0) refuse(taskId, 'invalid-scope', `${k} must be a nonempty string`);
|