hive-lite 0.1.9 → 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 +304 -31
- 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,34 +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'
|
|
312
551
|
&& !item.operations.includes('create_file')
|
|
313
|
-
&& item.
|
|
552
|
+
&& (!item.peerIdentity || identityMatches(item.peerIdentity, hypothesis.targetIdentity, identityAliases))
|
|
314
553
|
));
|
|
315
554
|
const referencePeers = capabilities.filter((item) => (
|
|
316
555
|
item.peerIdentity
|
|
317
556
|
&& hypothesis.targetIdentity
|
|
318
|
-
&& !identityMatches(item.peerIdentity, hypothesis.targetIdentity)
|
|
557
|
+
&& !identityMatches(item.peerIdentity, hypothesis.targetIdentity, identityAliases)
|
|
319
558
|
));
|
|
320
559
|
const selectedCreateCapabilities = hypothesis.createRequires.length
|
|
321
560
|
? createCapabilities.filter((item) => hypothesis.createRequires.some((artifact) => artifactMatches(item, artifact)))
|
|
@@ -376,13 +615,13 @@ function buildWritePlan(scope, intent, area) {
|
|
|
376
615
|
&& !hypothesis.targetAll
|
|
377
616
|
) {
|
|
378
617
|
const targetMatches = capabilities.filter((item) => (
|
|
379
|
-
item.peerIdentity && identityMatches(item.peerIdentity, hypothesis.targetIdentity)
|
|
618
|
+
item.peerIdentity && identityMatches(item.peerIdentity, hypothesis.targetIdentity, identityAliases)
|
|
380
619
|
));
|
|
381
620
|
const genericCapabilities = capabilities.filter((item) => (
|
|
382
621
|
!item.peerIdentity && !item.operations.includes('create_file')
|
|
383
622
|
));
|
|
384
623
|
const referencePeers = capabilities.filter((item) => (
|
|
385
|
-
item.peerIdentity && !identityMatches(item.peerIdentity, hypothesis.targetIdentity)
|
|
624
|
+
item.peerIdentity && !identityMatches(item.peerIdentity, hypothesis.targetIdentity, identityAliases)
|
|
386
625
|
));
|
|
387
626
|
selected = unique([...targetMatches, ...genericCapabilities].map((item) => item.scope));
|
|
388
627
|
for (const capability of referencePeers) {
|
|
@@ -400,11 +639,31 @@ function buildWritePlan(scope, intent, area) {
|
|
|
400
639
|
}
|
|
401
640
|
|
|
402
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
|
+
}
|
|
403
660
|
return {
|
|
404
661
|
hypotheses: [hypothesis],
|
|
405
662
|
capabilities,
|
|
406
663
|
selectedWritableDirect: selected,
|
|
664
|
+
selectedWritableCertificates,
|
|
407
665
|
referenceFiles: references,
|
|
666
|
+
peerFileCandidates,
|
|
408
667
|
blockingWarnings: warnings.filter((item) => item.blocking !== false),
|
|
409
668
|
warnings,
|
|
410
669
|
};
|
|
@@ -780,8 +1039,8 @@ function candidatePhaseSeeds(scored, intent) {
|
|
|
780
1039
|
phaseHint: area.name || area.id,
|
|
781
1040
|
findIntent: `${intent} [focus: ${area.id}]`,
|
|
782
1041
|
roles,
|
|
783
|
-
evidenceClasses: evidenceClassesForRoles(roles),
|
|
784
|
-
validationProfiles: ((area.validation || {}).profiles || []).filter(Boolean),
|
|
1042
|
+
evidenceClasses: unique(evidenceClassesForRoles(roles)),
|
|
1043
|
+
validationProfiles: unique(((area.validation || {}).profiles || []).filter(Boolean)),
|
|
785
1044
|
risk: riskLevelFor(area),
|
|
786
1045
|
reason: item.signals.slice(0, 4).join(', '),
|
|
787
1046
|
};
|
|
@@ -826,14 +1085,14 @@ function buildSplitMarkdown(note) {
|
|
|
826
1085
|
'',
|
|
827
1086
|
`Area: ${phase.primaryAreaId}`,
|
|
828
1087
|
'',
|
|
829
|
-
'
|
|
1088
|
+
'Copy one command block below to run this phase. Do not append the notes after it.',
|
|
830
1089
|
'',
|
|
831
1090
|
'```bash',
|
|
832
1091
|
findCommandForPhase(currentArgv, note, phase),
|
|
833
1092
|
'```',
|
|
834
1093
|
'',
|
|
835
1094
|
...(!sameArgv(currentArgv, packageAliasArgv) ? [
|
|
836
|
-
'
|
|
1095
|
+
'Or, if the package alias is installed:',
|
|
837
1096
|
'',
|
|
838
1097
|
'```bash',
|
|
839
1098
|
findCommandForPhase(packageAliasArgv, note, phase),
|
|
@@ -895,10 +1154,10 @@ function createSplitNote(root, packet, phaseSeeds, decomposition, options = {})
|
|
|
895
1154
|
title: phase.phaseHint,
|
|
896
1155
|
findIntent: phase.findIntent,
|
|
897
1156
|
primaryAreaId: phase.areaId,
|
|
898
|
-
expectedEvidence: [
|
|
1157
|
+
expectedEvidence: unique([
|
|
899
1158
|
...(phase.validationProfiles || []).map((profile) => `validation:${profile}`),
|
|
900
1159
|
...(phase.evidenceClasses || []).map((item) => `evidence:${item}`),
|
|
901
|
-
],
|
|
1160
|
+
]),
|
|
902
1161
|
nonGoals: phaseSeeds
|
|
903
1162
|
.filter((other) => other.areaId !== phase.areaId)
|
|
904
1163
|
.map((other) => `Do not change ${other.areaId} in this phase.`),
|
|
@@ -1113,6 +1372,8 @@ function hasBlockingMapGapWarning(warnings = []) {
|
|
|
1113
1372
|
'TARGET_ENTITY_MISMATCH',
|
|
1114
1373
|
'MISSING_ARTIFACT_FAMILY_SCOPE',
|
|
1115
1374
|
'MISSING_REQUIRED_HOOK_SCOPE',
|
|
1375
|
+
'AMBIGUOUS_TARGET_IDENTITY',
|
|
1376
|
+
'LOW_CONFIDENCE_INTENT_WRITE_PLAN',
|
|
1116
1377
|
].includes(warning.code));
|
|
1117
1378
|
}
|
|
1118
1379
|
|
|
@@ -1241,11 +1502,21 @@ function buildContextMarkdown(packet) {
|
|
|
1241
1502
|
: ['- Coverage: direct writable scope covers the inferred write operation.']),
|
|
1242
1503
|
'',
|
|
1243
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
|
+
] : []),
|
|
1244
1510
|
...(packet.writePlan && packet.writePlan.referenceFiles.length ? [
|
|
1245
1511
|
'## Reference Files',
|
|
1246
1512
|
...packet.writePlan.referenceFiles.map((file) => `- ${file.path}: ${file.reason}`),
|
|
1247
1513
|
'',
|
|
1248
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
|
+
] : []),
|
|
1249
1520
|
'## Relevant Files',
|
|
1250
1521
|
...packet.relevantFiles.map((file) => `- ${file.path} (${file.role || file.source}): ${file.reason}`),
|
|
1251
1522
|
'',
|
|
@@ -1337,7 +1608,9 @@ function createContextPacket(root, intent, options = {}) {
|
|
|
1337
1608
|
hypotheses: [],
|
|
1338
1609
|
capabilities: [],
|
|
1339
1610
|
selectedWritableDirect: [],
|
|
1611
|
+
selectedWritableCertificates: [],
|
|
1340
1612
|
referenceFiles: [],
|
|
1613
|
+
peerFileCandidates: [],
|
|
1341
1614
|
blockingWarnings: [],
|
|
1342
1615
|
warnings: [],
|
|
1343
1616
|
};
|