hive-lite 0.1.8 → 0.2.0
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/README.md +3 -1
- package/docs/cli-semantics.md +53 -1
- package/docs/skills/hive-lite-bootstrap/SKILL.md +28 -0
- package/docs/skills/hive-lite-discovery/SKILL.md +151 -0
- package/docs/skills/hive-lite-discovery/agents/openai.yaml +4 -0
- package/docs/skills/hive-lite-finish/SKILL.md +62 -5
- package/docs/skills/hive-lite-map-maintainer/SKILL.md +28 -0
- package/docs/skills/hive-lite-map-maintainer/references/lifecycle.md +1 -1
- package/docs/skills/hive-lite-map-maintainer/references/repair-rules.md +7 -0
- package/docs/skills/hive-lite-start-prompt/SKILL.md +175 -52
- package/docs/skills/hive-lite-start-prompt/references/preflight.md +70 -20
- package/package.json +3 -3
- package/src/cli.js +146 -3
- package/src/lib/change.js +93 -0
- package/src/lib/context.js +312 -35
- package/src/lib/git.js +1 -0
- package/src/lib/health.js +208 -0
- package/src/lib/intent.js +620 -0
- package/src/lib/map.js +57 -0
- package/src/lib/operator.js +1067 -0
- package/src/lib/skills.js +1 -0
package/src/lib/context.js
CHANGED
|
@@ -54,7 +54,7 @@ function isCreateCapablePattern(pattern) {
|
|
|
54
54
|
return !looksLikeFilePattern(pattern);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const
|
|
57
|
+
const DEFAULT_IDENTITY_ALIASES = {
|
|
58
58
|
openai: ['openai', 'chatgpt', 'gpt'],
|
|
59
59
|
google: ['google', 'gemini'],
|
|
60
60
|
qwen: ['qwen', 'tongyi'],
|
|
@@ -64,37 +64,101 @@ const IDENTITY_ALIASES = {
|
|
|
64
64
|
azure: ['azure'],
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
function
|
|
67
|
+
function identityAliasList(config) {
|
|
68
|
+
if (Array.isArray(config)) return arrayFrom(config);
|
|
69
|
+
if (config && typeof config === 'object') {
|
|
70
|
+
return [
|
|
71
|
+
...arrayFrom(config.aliases || config.alias),
|
|
72
|
+
...arrayFrom(config.tokens),
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
return arrayFrom(config);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function identityDisplayName(identity, area) {
|
|
79
|
+
const raw = area && (area.identity_aliases || area.identityAliases) ? area.identity_aliases || area.identityAliases : null;
|
|
80
|
+
if (raw) {
|
|
81
|
+
const entries = Array.isArray(raw)
|
|
82
|
+
? raw.map((item) => [item && (item.id || item.name || item.canonical), item])
|
|
83
|
+
: Object.entries(raw);
|
|
84
|
+
for (const [id, config] of entries) {
|
|
85
|
+
const candidate = (config && typeof config === 'object' ? config.canonical || config.id || config.name : null) || id;
|
|
86
|
+
if (normalize(candidate) === normalize(identity)) return String(candidate);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return normalize(identity).split(/[^a-z0-9]+/).filter(Boolean)
|
|
90
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
91
|
+
.join('') || String(identity || '');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function aliasRegistryForArea(area) {
|
|
95
|
+
const registry = Object.fromEntries(Object.entries(DEFAULT_IDENTITY_ALIASES)
|
|
96
|
+
.map(([canonical, aliases]) => [canonical, unique([canonical, ...aliases.map(normalize)])]));
|
|
97
|
+
const raw = area && (area.identity_aliases || area.identityAliases) ? area.identity_aliases || area.identityAliases : null;
|
|
98
|
+
if (!raw) return registry;
|
|
99
|
+
|
|
100
|
+
const entries = Array.isArray(raw)
|
|
101
|
+
? raw.map((item) => [item && (item.id || item.name || item.canonical), item])
|
|
102
|
+
: Object.entries(raw);
|
|
103
|
+
for (const [id, config] of entries) {
|
|
104
|
+
if (!config) continue;
|
|
105
|
+
const canonical = normalize(
|
|
106
|
+
(typeof config === 'object' ? config.canonical || config.id || config.name : null)
|
|
107
|
+
|| id
|
|
108
|
+
);
|
|
109
|
+
if (!canonical) continue;
|
|
110
|
+
const aliases = identityAliasList(config);
|
|
111
|
+
registry[canonical] = unique([
|
|
112
|
+
...(registry[canonical] || []),
|
|
113
|
+
canonical,
|
|
114
|
+
normalize(id),
|
|
115
|
+
...aliases.map(normalize),
|
|
116
|
+
].filter(Boolean));
|
|
117
|
+
}
|
|
118
|
+
return registry;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function isKnownIdentity(value, aliases = DEFAULT_IDENTITY_ALIASES) {
|
|
122
|
+
return Object.prototype.hasOwnProperty.call(aliases, value);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function canonicalIdentity(value, aliases = DEFAULT_IDENTITY_ALIASES) {
|
|
68
126
|
const text = normalize(value);
|
|
69
127
|
if (!text) return null;
|
|
70
|
-
for (const [canonical,
|
|
71
|
-
if (
|
|
128
|
+
for (const [canonical, values] of Object.entries(aliases)) {
|
|
129
|
+
if (canonical === text || values.includes(text)) return canonical;
|
|
72
130
|
}
|
|
73
131
|
return text;
|
|
74
132
|
}
|
|
75
133
|
|
|
76
|
-
function targetIdentityForIntent(intent) {
|
|
134
|
+
function targetIdentityForIntent(intent, aliases) {
|
|
77
135
|
const tokens = tokenize(intent);
|
|
78
136
|
for (const token of tokens) {
|
|
79
|
-
const identity = canonicalIdentity(token);
|
|
80
|
-
if (identity &&
|
|
137
|
+
const identity = canonicalIdentity(token, aliases);
|
|
138
|
+
if (identity && isKnownIdentity(identity, aliases)) return identity;
|
|
81
139
|
}
|
|
82
140
|
return null;
|
|
83
141
|
}
|
|
84
142
|
|
|
85
|
-
function
|
|
143
|
+
function targetIdentitiesForIntent(intent, aliases) {
|
|
144
|
+
return unique(tokenize(intent)
|
|
145
|
+
.map((token) => canonicalIdentity(token, aliases))
|
|
146
|
+
.filter((identity) => identity && isKnownIdentity(identity, aliases)));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function peerIdentityFromPath(pattern, aliases) {
|
|
86
150
|
if (!looksLikeFilePattern(pattern)) return null;
|
|
87
151
|
const tokens = basenameTokens(pattern);
|
|
88
152
|
for (const token of tokens) {
|
|
89
|
-
const identity = canonicalIdentity(token);
|
|
90
|
-
if (identity &&
|
|
153
|
+
const identity = canonicalIdentity(token, aliases);
|
|
154
|
+
if (identity && isKnownIdentity(identity, aliases)) return identity;
|
|
91
155
|
}
|
|
92
156
|
return null;
|
|
93
157
|
}
|
|
94
158
|
|
|
95
|
-
function identityMatches(left, right) {
|
|
159
|
+
function identityMatches(left, right, aliases) {
|
|
96
160
|
if (!left || !right) return false;
|
|
97
|
-
return canonicalIdentity(left) === canonicalIdentity(right);
|
|
161
|
+
return canonicalIdentity(left, aliases) === canonicalIdentity(right, aliases);
|
|
98
162
|
}
|
|
99
163
|
|
|
100
164
|
function intentTargetsAllPeers(intent) {
|
|
@@ -141,6 +205,50 @@ function configuredArtifactFamilies(area) {
|
|
|
141
205
|
}));
|
|
142
206
|
}
|
|
143
207
|
|
|
208
|
+
function configuredPeerFileTemplates(area) {
|
|
209
|
+
const raw = area && (area.peer_file_templates || area.peerFileTemplates) ? area.peer_file_templates || area.peerFileTemplates : null;
|
|
210
|
+
if (!raw) return [];
|
|
211
|
+
const groups = Array.isArray(raw)
|
|
212
|
+
? raw.map((item) => [item && (item.id || item.name || item.artifact_family || item.artifactFamily), item])
|
|
213
|
+
: Object.entries(raw);
|
|
214
|
+
const templates = [];
|
|
215
|
+
for (const [id, value] of groups) {
|
|
216
|
+
if (!value) continue;
|
|
217
|
+
const group = typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
218
|
+
const artifacts = group.artifacts || group.files || group.templates || (typeof value === 'object' && !Array.isArray(value) ? value : {});
|
|
219
|
+
for (const [artifactId, artifactValue] of Object.entries(artifacts || {})) {
|
|
220
|
+
const template = typeof artifactValue === 'string'
|
|
221
|
+
? artifactValue
|
|
222
|
+
: artifactValue && (artifactValue.path || artifactValue.template || artifactValue.pattern);
|
|
223
|
+
if (!template) continue;
|
|
224
|
+
templates.push({
|
|
225
|
+
id: id || group.id || group.name || '',
|
|
226
|
+
artifact: typeof artifactValue === 'object' && artifactValue.artifact ? artifactValue.artifact : artifactId,
|
|
227
|
+
artifactFamily: group.artifact_family || group.artifactFamily || id || '',
|
|
228
|
+
targetSlot: group.target_slot || group.targetSlot || null,
|
|
229
|
+
template: String(template),
|
|
230
|
+
intentKinds: arrayFrom(group.intent_kinds || group.intentKinds),
|
|
231
|
+
reason: (typeof artifactValue === 'object' && artifactValue.reason) || group.reason || '',
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return templates;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function peerTemplateFamilyMatches(template, hypothesis) {
|
|
239
|
+
const templateFamily = normalize(template.artifactFamily).replace(/-/g, '_');
|
|
240
|
+
const artifactFamily = normalize(hypothesis.artifactFamily).replace(/-/g, '_');
|
|
241
|
+
if (!templateFamily || !artifactFamily || artifactFamily === 'unknown') return true;
|
|
242
|
+
return templateFamily === artifactFamily || templateFamily.endsWith(artifactFamily);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function peerTemplateApplies(template, hypothesis) {
|
|
246
|
+
if (template.intentKinds.length > 0 && !template.intentKinds.includes(hypothesis.intentKind)) return false;
|
|
247
|
+
if (!peerTemplateFamilyMatches(template, hypothesis)) return false;
|
|
248
|
+
if (hypothesis.createRequires.length > 0 && !hypothesis.createRequires.includes(template.artifact)) return false;
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
|
|
144
252
|
function configuredArtifactFamilyForIntent(area, intent) {
|
|
145
253
|
const text = normalize(intent);
|
|
146
254
|
for (const family of configuredArtifactFamilies(area)) {
|
|
@@ -189,10 +297,15 @@ function classifyArtifactFamily(intent, area) {
|
|
|
189
297
|
}
|
|
190
298
|
|
|
191
299
|
function classifyIntentWrite(intent, area) {
|
|
300
|
+
const identityAliases = aliasRegistryForArea(area);
|
|
192
301
|
const text = normalize(intent);
|
|
193
|
-
const
|
|
194
|
-
/\b(
|
|
195
|
-
|
|
302
|
+
const strongCreateTerm = [
|
|
303
|
+
/\b(create|new|introduce|implement)\b/,
|
|
304
|
+
/新增|新建|创建|接入/,
|
|
305
|
+
].some((pattern) => pattern.test(text));
|
|
306
|
+
const createTerm = strongCreateTerm || [
|
|
307
|
+
/\b(add|support)\b/,
|
|
308
|
+
/添加|支持/,
|
|
196
309
|
].some((pattern) => pattern.test(text));
|
|
197
310
|
const deleteTerm = [
|
|
198
311
|
/\b(delete|remove)\b/,
|
|
@@ -205,7 +318,8 @@ function classifyIntentWrite(intent, area) {
|
|
|
205
318
|
const targetAll = intentTargetsAllPeers(intent);
|
|
206
319
|
const artifactFamily = classifyArtifactFamily(intent, area);
|
|
207
320
|
const familyConfig = configuredArtifactFamilies(area).find((item) => item.id === artifactFamily) || null;
|
|
208
|
-
const targetIdentity = targetIdentityForIntent(intent);
|
|
321
|
+
const targetIdentity = targetIdentityForIntent(intent, identityAliases);
|
|
322
|
+
const targetIdentities = targetIdentitiesForIntent(intent, identityAliases);
|
|
209
323
|
let action = 'modify';
|
|
210
324
|
if (deleteTerm) action = 'delete';
|
|
211
325
|
else if (renameTerm) action = 'rename';
|
|
@@ -214,6 +328,7 @@ function classifyIntentWrite(intent, area) {
|
|
|
214
328
|
action,
|
|
215
329
|
artifactFamily,
|
|
216
330
|
targetIdentity,
|
|
331
|
+
targetIdentities,
|
|
217
332
|
operation: action === 'create' && artifactFamily === 'provider_proxy' ? 'create_file' : 'modify_existing_file',
|
|
218
333
|
required: true,
|
|
219
334
|
targetAll,
|
|
@@ -221,10 +336,11 @@ function classifyIntentWrite(intent, area) {
|
|
|
221
336
|
createRequires: familyConfig ? familyConfig.createRequires : [],
|
|
222
337
|
hooks: familyConfig ? familyConfig.hooks : [],
|
|
223
338
|
confidence: artifactFamily === 'unknown' ? 'low' : 'medium',
|
|
339
|
+
strongCreateIntent: strongCreateTerm,
|
|
224
340
|
};
|
|
225
341
|
}
|
|
226
342
|
|
|
227
|
-
function directCapabilities(scope) {
|
|
343
|
+
function directCapabilities(scope, identityAliases) {
|
|
228
344
|
const explicitItems = [
|
|
229
345
|
...(scope.writableDirectItems || []),
|
|
230
346
|
];
|
|
@@ -248,12 +364,14 @@ function directCapabilities(scope) {
|
|
|
248
364
|
breadth: createCapable ? 'narrow_pattern' : 'exact',
|
|
249
365
|
reviewGated: false,
|
|
250
366
|
source: item.source || 'writable_direct',
|
|
367
|
+
operationSpecified: configuredOperations.length > 0,
|
|
251
368
|
artifact: item.artifact || null,
|
|
252
369
|
artifactFamily: item.artifactFamily || item.artifact_family || null,
|
|
253
370
|
intentKinds: arrayFrom(item.intentKinds || item.intent_kinds),
|
|
254
371
|
requiredWhen: arrayFrom(item.requiredWhen || item.required_when),
|
|
255
372
|
targetSlot: item.targetSlot || item.target_slot || null,
|
|
256
|
-
peerIdentity: canonicalIdentity(item.targetIdentity || item.target_identity || item.peerIdentity || item.peer_identity
|
|
373
|
+
peerIdentity: canonicalIdentity(item.targetIdentity || item.target_identity || item.peerIdentity || item.peer_identity, identityAliases)
|
|
374
|
+
|| peerIdentityFromPath(pattern, identityAliases),
|
|
257
375
|
};
|
|
258
376
|
}).filter(Boolean);
|
|
259
377
|
}
|
|
@@ -288,32 +406,155 @@ function capabilityMatchesIntent(capability, hypothesis) {
|
|
|
288
406
|
return capability.intentKinds.includes(hypothesis.intentKind);
|
|
289
407
|
}
|
|
290
408
|
|
|
409
|
+
function expandPeerTemplate(template, hypothesis, area) {
|
|
410
|
+
if (!hypothesis.targetIdentity) return template;
|
|
411
|
+
const label = identityDisplayName(hypothesis.targetIdentity, area);
|
|
412
|
+
const lower = normalize(hypothesis.targetIdentity);
|
|
413
|
+
return template
|
|
414
|
+
.replace(/\{Provider\}|\{Identity\}|\{Target\}/g, label)
|
|
415
|
+
.replace(/\{provider\}|\{identity\}|\{target\}/g, lower)
|
|
416
|
+
.replace(/\{PROVIDER\}|\{IDENTITY\}|\{TARGET\}/g, lower.toUpperCase());
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function peerFileCandidatesFor(area, hypothesis) {
|
|
420
|
+
return configuredPeerFileTemplates(area)
|
|
421
|
+
.filter((item) => peerTemplateApplies(item, hypothesis))
|
|
422
|
+
.map((item) => ({
|
|
423
|
+
artifact: item.artifact,
|
|
424
|
+
artifactFamily: item.artifactFamily,
|
|
425
|
+
targetSlot: item.targetSlot,
|
|
426
|
+
targetIdentity: hypothesis.targetIdentity || null,
|
|
427
|
+
template: item.template,
|
|
428
|
+
candidatePath: expandPeerTemplate(item.template, hypothesis, area),
|
|
429
|
+
writable: false,
|
|
430
|
+
reason: item.reason || 'peer_file_templates are read-only hints; use writable_create_patterns to grant create permission',
|
|
431
|
+
}));
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function selectedCapabilitiesFor(capabilities, selected) {
|
|
435
|
+
const selectedSet = new Set(selected);
|
|
436
|
+
return capabilities.filter((item) => selectedSet.has(item.scope));
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function directReferenceConflicts(references, capabilities) {
|
|
440
|
+
const conflicts = [];
|
|
441
|
+
for (const reference of references) {
|
|
442
|
+
const referencePath = reference.path || reference.pattern;
|
|
443
|
+
if (!referencePath) continue;
|
|
444
|
+
const capability = capabilities.find((item) => (
|
|
445
|
+
item.source !== 'writable_create_patterns'
|
|
446
|
+
&& item.operations.includes('update_existing')
|
|
447
|
+
&& matchesPattern(referencePath, item.scope)
|
|
448
|
+
));
|
|
449
|
+
if (capability) {
|
|
450
|
+
conflicts.push({
|
|
451
|
+
path: referencePath,
|
|
452
|
+
directScope: capability.scope,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return conflicts;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function unspecifiedOperationScopes(capabilities, hypothesis) {
|
|
460
|
+
return capabilities
|
|
461
|
+
.filter((item) => (
|
|
462
|
+
item.source === 'writable_direct'
|
|
463
|
+
&& item.operationSpecified === false
|
|
464
|
+
&& (hypothesis.operation === 'create_file' || item.operations.includes('create_file'))
|
|
465
|
+
))
|
|
466
|
+
.map((item) => item.scope);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function certificateOperation(capability, hypothesis) {
|
|
470
|
+
if (hypothesis.operation === 'create_file' && capability.operations.includes('create_file')) return 'create_file';
|
|
471
|
+
if (capability.operations.includes('update_existing')) return 'update_existing';
|
|
472
|
+
return capability.operations[0] || hypothesis.operation || 'update_existing';
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function certificateMapEntry(area, capability) {
|
|
476
|
+
const areaId = area && area.id ? area.id : 'unknown_area';
|
|
477
|
+
const source = capability.source || 'writable_direct';
|
|
478
|
+
const label = capability.artifact || capability.artifactFamily || null;
|
|
479
|
+
return label ? `${areaId}.scope.${source}.${label}` : `${areaId}.scope.${source}`;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function buildCapabilityCertificate(area, capability, hypothesis) {
|
|
483
|
+
const operation = certificateOperation(capability, hypothesis);
|
|
484
|
+
return {
|
|
485
|
+
path: capability.scope,
|
|
486
|
+
scope: capability.scope,
|
|
487
|
+
operation,
|
|
488
|
+
artifact: capability.artifact || null,
|
|
489
|
+
artifactFamily: capability.artifactFamily || hypothesis.artifactFamily || null,
|
|
490
|
+
targetIdentity: capability.peerIdentity || hypothesis.targetIdentity || null,
|
|
491
|
+
status: 'certified',
|
|
492
|
+
certifiedBy: {
|
|
493
|
+
mapEntry: certificateMapEntry(area, capability),
|
|
494
|
+
source: capability.source || 'writable_direct',
|
|
495
|
+
pattern: capability.scope,
|
|
496
|
+
intentKind: hypothesis.intentKind,
|
|
497
|
+
},
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function certificatesForSelected(area, capabilities, selected, hypothesis) {
|
|
502
|
+
return selectedCapabilitiesFor(capabilities, selected).map((capability) => (
|
|
503
|
+
buildCapabilityCertificate(area, capability, hypothesis)
|
|
504
|
+
));
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/*
|
|
508
|
+
* Write permission is certified here, not by relevance alone:
|
|
509
|
+
* relevant files, entrypoints, peer examples, and broad fallback scopes may help
|
|
510
|
+
* an agent understand the repo, but they do not become Direct Writable unless
|
|
511
|
+
* they cover the inferred required write operation narrowly for this intent.
|
|
512
|
+
*/
|
|
291
513
|
function buildWritePlan(scope, intent, area) {
|
|
514
|
+
const identityAliases = aliasRegistryForArea(area);
|
|
292
515
|
const hypothesis = classifyIntentWrite(intent, area);
|
|
293
|
-
const capabilities = directCapabilities(scope);
|
|
516
|
+
const capabilities = directCapabilities(scope, identityAliases);
|
|
294
517
|
const warnings = [];
|
|
295
518
|
const references = (scope.readableReference || []).map((item) => buildReference(
|
|
296
519
|
{
|
|
297
520
|
path: item.path,
|
|
298
521
|
role: item.role || 'reference',
|
|
299
522
|
artifact: item.artifact || null,
|
|
300
|
-
peerIdentity: canonicalIdentity(item.peerIdentity) || peerIdentityFromPath(item.path),
|
|
523
|
+
peerIdentity: canonicalIdentity(item.peerIdentity, identityAliases) || peerIdentityFromPath(item.path, identityAliases),
|
|
301
524
|
},
|
|
302
525
|
item.reason || 'readable reference from Project Map'
|
|
303
526
|
));
|
|
527
|
+
const peerFileCandidates = peerFileCandidatesFor(area, hypothesis);
|
|
304
528
|
let selected = capabilities.map((item) => item.scope);
|
|
305
529
|
|
|
530
|
+
if (hypothesis.targetIdentities && hypothesis.targetIdentities.length > 1 && !hypothesis.targetAll) {
|
|
531
|
+
warnings.push(warning(
|
|
532
|
+
'AMBIGUOUS_TARGET_IDENTITY',
|
|
533
|
+
`Intent mentions multiple target identities (${hypothesis.targetIdentities.join(', ')}); Direct Writable must be selected for one unambiguous target.`,
|
|
534
|
+
{ targetIdentities: hypothesis.targetIdentities }
|
|
535
|
+
));
|
|
536
|
+
}
|
|
537
|
+
if (hypothesis.confidence === 'low' && hypothesis.action !== 'modify' && (hypothesis.strongCreateIntent || hypothesis.action !== 'create')) {
|
|
538
|
+
warnings.push(warning(
|
|
539
|
+
'LOW_CONFIDENCE_INTENT_WRITE_PLAN',
|
|
540
|
+
'Intent appears to require writes, but Hive Lite could not infer a known artifact family for the required write plan.',
|
|
541
|
+
{ action: hypothesis.action, artifactFamily: hypothesis.artifactFamily }
|
|
542
|
+
));
|
|
543
|
+
}
|
|
544
|
+
|
|
306
545
|
if (hypothesis.operation === 'create_file' && hypothesis.artifactFamily === 'provider_proxy') {
|
|
307
546
|
const createCapabilities = capabilities.filter((item) => (
|
|
308
547
|
item.operations.includes('create_file') && capabilityMatchesIntent(item, hypothesis)
|
|
309
548
|
));
|
|
310
549
|
const genericExistingCapabilities = capabilities.filter((item) => (
|
|
311
|
-
|
|
550
|
+
item.source === 'writable_existing'
|
|
551
|
+
&& !item.operations.includes('create_file')
|
|
552
|
+
&& (!item.peerIdentity || identityMatches(item.peerIdentity, hypothesis.targetIdentity, identityAliases))
|
|
312
553
|
));
|
|
313
554
|
const referencePeers = capabilities.filter((item) => (
|
|
314
555
|
item.peerIdentity
|
|
315
556
|
&& hypothesis.targetIdentity
|
|
316
|
-
&& !identityMatches(item.peerIdentity, hypothesis.targetIdentity)
|
|
557
|
+
&& !identityMatches(item.peerIdentity, hypothesis.targetIdentity, identityAliases)
|
|
317
558
|
));
|
|
318
559
|
const selectedCreateCapabilities = hypothesis.createRequires.length
|
|
319
560
|
? createCapabilities.filter((item) => hypothesis.createRequires.some((artifact) => artifactMatches(item, artifact)))
|
|
@@ -374,13 +615,13 @@ function buildWritePlan(scope, intent, area) {
|
|
|
374
615
|
&& !hypothesis.targetAll
|
|
375
616
|
) {
|
|
376
617
|
const targetMatches = capabilities.filter((item) => (
|
|
377
|
-
item.peerIdentity && identityMatches(item.peerIdentity, hypothesis.targetIdentity)
|
|
618
|
+
item.peerIdentity && identityMatches(item.peerIdentity, hypothesis.targetIdentity, identityAliases)
|
|
378
619
|
));
|
|
379
620
|
const genericCapabilities = capabilities.filter((item) => (
|
|
380
621
|
!item.peerIdentity && !item.operations.includes('create_file')
|
|
381
622
|
));
|
|
382
623
|
const referencePeers = capabilities.filter((item) => (
|
|
383
|
-
item.peerIdentity && !identityMatches(item.peerIdentity, hypothesis.targetIdentity)
|
|
624
|
+
item.peerIdentity && !identityMatches(item.peerIdentity, hypothesis.targetIdentity, identityAliases)
|
|
384
625
|
));
|
|
385
626
|
selected = unique([...targetMatches, ...genericCapabilities].map((item) => item.scope));
|
|
386
627
|
for (const capability of referencePeers) {
|
|
@@ -398,11 +639,31 @@ function buildWritePlan(scope, intent, area) {
|
|
|
398
639
|
}
|
|
399
640
|
|
|
400
641
|
selected = unique(selected);
|
|
642
|
+
const selectedCapabilities = selectedCapabilitiesFor(capabilities, selected);
|
|
643
|
+
const selectedWritableCertificates = certificatesForSelected(area, capabilities, selected, hypothesis);
|
|
644
|
+
const referenceConflicts = directReferenceConflicts(references, selectedCapabilities);
|
|
645
|
+
if (referenceConflicts.length > 0) {
|
|
646
|
+
warnings.push(warning(
|
|
647
|
+
'REFERENCE_DIRECT_ROLE_CONFLICT',
|
|
648
|
+
'One or more reference files also match selected Direct Writable update scope; keep peer examples reference-only or move new-file permission to writable_create_patterns.',
|
|
649
|
+
{ blocking: false, conflicts: referenceConflicts }
|
|
650
|
+
));
|
|
651
|
+
}
|
|
652
|
+
const unspecifiedScopes = unspecifiedOperationScopes(selectedCapabilities, hypothesis);
|
|
653
|
+
if (unspecifiedScopes.length > 0) {
|
|
654
|
+
warnings.push(warning(
|
|
655
|
+
'DIRECT_SCOPE_OPERATION_UNSPECIFIED',
|
|
656
|
+
'Selected Direct Writable scope relies on inferred operations; declare operations under writable_existing or writable_create_patterns for safer write certification.',
|
|
657
|
+
{ blocking: false, scopes: unspecifiedScopes }
|
|
658
|
+
));
|
|
659
|
+
}
|
|
401
660
|
return {
|
|
402
661
|
hypotheses: [hypothesis],
|
|
403
662
|
capabilities,
|
|
404
663
|
selectedWritableDirect: selected,
|
|
664
|
+
selectedWritableCertificates,
|
|
405
665
|
referenceFiles: references,
|
|
666
|
+
peerFileCandidates,
|
|
406
667
|
blockingWarnings: warnings.filter((item) => item.blocking !== false),
|
|
407
668
|
warnings,
|
|
408
669
|
};
|
|
@@ -677,9 +938,10 @@ function competingHighScoringAreas(scored) {
|
|
|
677
938
|
));
|
|
678
939
|
}
|
|
679
940
|
|
|
680
|
-
function decompositionSignals({ map, area, confidence, intent, relevant, scope, scored, validationPlan, constrainedAreaId }) {
|
|
941
|
+
function decompositionSignals({ map, area, confidence, intent, relevant, scope, selectedWritableDirect, scored, validationPlan, constrainedAreaId }) {
|
|
681
942
|
if (!area) return [];
|
|
682
943
|
const signals = [];
|
|
944
|
+
const writableDirectForIntent = selectedWritableDirect || scope.writableDirect || [];
|
|
683
945
|
const highAreas = highScoringAreas(scored)
|
|
684
946
|
.filter((item) => !constrainedAreaId || item.area.id === constrainedAreaId);
|
|
685
947
|
const competingAreas = competingHighScoringAreas(scored)
|
|
@@ -711,16 +973,16 @@ function decompositionSignals({ map, area, confidence, intent, relevant, scope,
|
|
|
711
973
|
}));
|
|
712
974
|
}
|
|
713
975
|
|
|
714
|
-
if (
|
|
976
|
+
if (writableDirectForIntent.length > DECOMPOSITION_LIMITS.writableFilesBlocking) {
|
|
715
977
|
signals.push(signal('TOO_MANY_WRITABLE_FILES', 'Direct writable files exceed the safe change boundary.', {
|
|
716
|
-
count:
|
|
978
|
+
count: writableDirectForIntent.length,
|
|
717
979
|
threshold: DECOMPOSITION_LIMITS.writableFilesBlocking,
|
|
718
980
|
}));
|
|
719
|
-
} else if (
|
|
981
|
+
} else if (writableDirectForIntent.length > DECOMPOSITION_LIMITS.writableFilesWarning) {
|
|
720
982
|
signals.push(signal('TOO_MANY_WRITABLE_FILES', 'Direct writable files are high for one Change Record.', {
|
|
721
983
|
severity: 'warning',
|
|
722
984
|
blocking: false,
|
|
723
|
-
count:
|
|
985
|
+
count: writableDirectForIntent.length,
|
|
724
986
|
threshold: DECOMPOSITION_LIMITS.writableFilesWarning,
|
|
725
987
|
}));
|
|
726
988
|
}
|
|
@@ -777,8 +1039,8 @@ function candidatePhaseSeeds(scored, intent) {
|
|
|
777
1039
|
phaseHint: area.name || area.id,
|
|
778
1040
|
findIntent: `${intent} [focus: ${area.id}]`,
|
|
779
1041
|
roles,
|
|
780
|
-
evidenceClasses: evidenceClassesForRoles(roles),
|
|
781
|
-
validationProfiles: ((area.validation || {}).profiles || []).filter(Boolean),
|
|
1042
|
+
evidenceClasses: unique(evidenceClassesForRoles(roles)),
|
|
1043
|
+
validationProfiles: unique(((area.validation || {}).profiles || []).filter(Boolean)),
|
|
782
1044
|
risk: riskLevelFor(area),
|
|
783
1045
|
reason: item.signals.slice(0, 4).join(', '),
|
|
784
1046
|
};
|
|
@@ -823,14 +1085,14 @@ function buildSplitMarkdown(note) {
|
|
|
823
1085
|
'',
|
|
824
1086
|
`Area: ${phase.primaryAreaId}`,
|
|
825
1087
|
'',
|
|
826
|
-
'
|
|
1088
|
+
'Copy one command block below to run this phase. Do not append the notes after it.',
|
|
827
1089
|
'',
|
|
828
1090
|
'```bash',
|
|
829
1091
|
findCommandForPhase(currentArgv, note, phase),
|
|
830
1092
|
'```',
|
|
831
1093
|
'',
|
|
832
1094
|
...(!sameArgv(currentArgv, packageAliasArgv) ? [
|
|
833
|
-
'
|
|
1095
|
+
'Or, if the package alias is installed:',
|
|
834
1096
|
'',
|
|
835
1097
|
'```bash',
|
|
836
1098
|
findCommandForPhase(packageAliasArgv, note, phase),
|
|
@@ -892,10 +1154,10 @@ function createSplitNote(root, packet, phaseSeeds, decomposition, options = {})
|
|
|
892
1154
|
title: phase.phaseHint,
|
|
893
1155
|
findIntent: phase.findIntent,
|
|
894
1156
|
primaryAreaId: phase.areaId,
|
|
895
|
-
expectedEvidence: [
|
|
1157
|
+
expectedEvidence: unique([
|
|
896
1158
|
...(phase.validationProfiles || []).map((profile) => `validation:${profile}`),
|
|
897
1159
|
...(phase.evidenceClasses || []).map((item) => `evidence:${item}`),
|
|
898
|
-
],
|
|
1160
|
+
]),
|
|
899
1161
|
nonGoals: phaseSeeds
|
|
900
1162
|
.filter((other) => other.areaId !== phase.areaId)
|
|
901
1163
|
.map((other) => `Do not change ${other.areaId} in this phase.`),
|
|
@@ -1110,6 +1372,8 @@ function hasBlockingMapGapWarning(warnings = []) {
|
|
|
1110
1372
|
'TARGET_ENTITY_MISMATCH',
|
|
1111
1373
|
'MISSING_ARTIFACT_FAMILY_SCOPE',
|
|
1112
1374
|
'MISSING_REQUIRED_HOOK_SCOPE',
|
|
1375
|
+
'AMBIGUOUS_TARGET_IDENTITY',
|
|
1376
|
+
'LOW_CONFIDENCE_INTENT_WRITE_PLAN',
|
|
1113
1377
|
].includes(warning.code));
|
|
1114
1378
|
}
|
|
1115
1379
|
|
|
@@ -1238,11 +1502,21 @@ function buildContextMarkdown(packet) {
|
|
|
1238
1502
|
: ['- Coverage: direct writable scope covers the inferred write operation.']),
|
|
1239
1503
|
'',
|
|
1240
1504
|
] : []),
|
|
1505
|
+
...(packet.writePlan && packet.writePlan.selectedWritableCertificates && packet.writePlan.selectedWritableCertificates.length ? [
|
|
1506
|
+
'## Certified Direct Writable',
|
|
1507
|
+
...packet.writePlan.selectedWritableCertificates.map((item) => `- ${item.path}: ${item.operation}${item.artifact ? ` ${item.artifact}` : ''}${item.targetIdentity ? ` for ${item.targetIdentity}` : ''} (certified by ${item.certifiedBy && item.certifiedBy.mapEntry ? item.certifiedBy.mapEntry : 'unknown map entry'})`),
|
|
1508
|
+
'',
|
|
1509
|
+
] : []),
|
|
1241
1510
|
...(packet.writePlan && packet.writePlan.referenceFiles.length ? [
|
|
1242
1511
|
'## Reference Files',
|
|
1243
1512
|
...packet.writePlan.referenceFiles.map((file) => `- ${file.path}: ${file.reason}`),
|
|
1244
1513
|
'',
|
|
1245
1514
|
] : []),
|
|
1515
|
+
...(packet.writePlan && packet.writePlan.peerFileCandidates && packet.writePlan.peerFileCandidates.length ? [
|
|
1516
|
+
'## Peer File Template Candidates',
|
|
1517
|
+
...packet.writePlan.peerFileCandidates.map((file) => `- ${file.artifact}: ${file.candidatePath || file.template} (read-only hint; not Direct Writable)`),
|
|
1518
|
+
'',
|
|
1519
|
+
] : []),
|
|
1246
1520
|
'## Relevant Files',
|
|
1247
1521
|
...packet.relevantFiles.map((file) => `- ${file.path} (${file.role || file.source}): ${file.reason}`),
|
|
1248
1522
|
'',
|
|
@@ -1334,7 +1608,9 @@ function createContextPacket(root, intent, options = {}) {
|
|
|
1334
1608
|
hypotheses: [],
|
|
1335
1609
|
capabilities: [],
|
|
1336
1610
|
selectedWritableDirect: [],
|
|
1611
|
+
selectedWritableCertificates: [],
|
|
1337
1612
|
referenceFiles: [],
|
|
1613
|
+
peerFileCandidates: [],
|
|
1338
1614
|
blockingWarnings: [],
|
|
1339
1615
|
warnings: [],
|
|
1340
1616
|
};
|
|
@@ -1403,6 +1679,7 @@ function createContextPacket(root, intent, options = {}) {
|
|
|
1403
1679
|
intent,
|
|
1404
1680
|
relevant,
|
|
1405
1681
|
scope,
|
|
1682
|
+
selectedWritableDirect: writePlan.selectedWritableDirect,
|
|
1406
1683
|
scored,
|
|
1407
1684
|
validationPlan,
|
|
1408
1685
|
constrainedAreaId,
|