@ngockhoale/ukit 3.4.9 → 3.4.11
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
CHANGED
|
@@ -2,9 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to UKit are documented here.
|
|
4
4
|
|
|
5
|
+
## 3.4.10 - 2026-10-01
|
|
6
|
+
|
|
7
|
+
**sensitive-data-guard reads .env placeholders.** `classifySecretFile` now
|
|
8
|
+
exempts every dotenv name carrying a distributable marker
|
|
9
|
+
(`example|examples|sample|template|dist`) — `.env.example.local`,
|
|
10
|
+
`.env.production.example`, `.env.sample`, `.env.dist`, `.env.template`
|
|
11
|
+
alongside the old `.env.example` — for Read/Grep and Bash lanes alike.
|
|
12
|
+
Real env files (`.env`, `.env.local`, `.env.production`) stay blocked.
|
|
13
|
+
|
|
5
14
|
## 3.4.8 - 2026-10-01
|
|
6
15
|
|
|
7
|
-
(
|
|
16
|
+
(3.4.7 and 3.4.9 were lost to npm staged-version E409 limbo; 3.4.8 landed and its `latest` tag was assigned via `npm dist-tag`.)
|
|
8
17
|
|
|
9
18
|
|
|
10
19
|
**Completion gate no longer demands an edit for read-only questions.** The
|
package/package.json
CHANGED
|
@@ -201,7 +201,14 @@ async function evaluate({ rawInput, projectRoot, env }) {
|
|
|
201
201
|
const rawBase = normalized.split('/').pop();
|
|
202
202
|
if (!rawBase) return null;
|
|
203
203
|
const base = rawBase.toLowerCase();
|
|
204
|
-
|
|
204
|
+
// .env* placeholders that carry no secrets by convention: any dotenv file
|
|
205
|
+
// whose basename marks it a non-secret distributable — .env.example,
|
|
206
|
+
// .env.example.local, .env.examples, .env.sample, .env.dist,
|
|
207
|
+
// .env.template, .env.dev.example. The marker must appear after the
|
|
208
|
+
// '.env' prefix so a real env file can never dodge by containing the word
|
|
209
|
+
// earlier (there is nothing before '.env' in the basename anyway — the
|
|
210
|
+
// check is anchored on the dotenv name itself).
|
|
211
|
+
if (/^\.env[._-]?.*\b(example|examples|sample|template|dist)\b/i.test(base)) return null;
|
|
205
212
|
if (base === '.env' || base.startsWith('.env.')) return 'dotenv/secret env file';
|
|
206
213
|
if (base.endsWith('.pub') || base.endsWith('.public')) return null;
|
|
207
214
|
if (/^id_(rsa|dsa|ecdsa|ed25519)(\.|$)/.test(base)) return 'SSH private key';
|
|
@@ -2157,6 +2157,32 @@ setTimeout(() => process.exit(0), HOOK_DEADLINE_MS).unref();
|
|
|
2157
2157
|
return /(?<![A-Za-z0-9_])(?:implement|apply|update|modify|add|create|ship|deliver|fix|refactor|remove|delete|rename|change|write|build|make|install|run|deploy|execute|edit|sua|them|tao|xoa|doi|thay\s+the|cap\s+nhat|viet|chay|cai|chinh|trien\s+khai|cau\s+hinh)(?![A-Za-z0-9_])/.test(residue);
|
|
2158
2158
|
}
|
|
2159
2159
|
|
|
2160
|
+
// USER-REPORTED (2026-10-01): "chỉ lên plan cho tôi" / "just plan it" asks
|
|
2161
|
+
// for a plan and nothing more — the deliverable is a document in the reply,
|
|
2162
|
+
// not a mutation. mutationOrder=false alone does NOT cover these: they name
|
|
2163
|
+
// files and future work, so the router lands them on local-build/local-fix,
|
|
2164
|
+
// mints write debt, and the gate forces an implement the user never asked
|
|
2165
|
+
// for. Plan-only = explicit plan-request signal AND no surviving mutation
|
|
2166
|
+
// order ("lên plan rồi sửa luôn" is an implement request, not plan-only).
|
|
2167
|
+
function isPlanOnlyRequest({ promptText = '', commandText = '' } = {}) {
|
|
2168
|
+
const folded = `${promptText ?? ''}\n${commandText ?? ''}`
|
|
2169
|
+
.toLowerCase()
|
|
2170
|
+
.normalize('NFD')
|
|
2171
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
2172
|
+
.replace(/\u0111/g, 'd');
|
|
2173
|
+
const trimmed = folded.trim();
|
|
2174
|
+
if (!trimmed) return false;
|
|
2175
|
+
const planSignal =
|
|
2176
|
+
/\b(?:chi|just|only)\s+(?:len|lap|vach|lam)\b[^.,;\n]{0,40}\b(?:plan|ke\s+hoach|phuong\s+an|lo\s+trinh|roadmap|blueprint)\b/.test(trimmed)
|
|
2177
|
+
|| /\b(?:len|lap|vach|de\s+xuat|draft)\s+(?:ke\s+hoach|plan|phuong\s+an|lo\s+trinh|roadmap)\b/.test(trimmed)
|
|
2178
|
+
|| /\b(?:plan|planning)\s+(?:only|first|mode)\b/.test(trimmed)
|
|
2179
|
+
|| /\b(?:ke\s+hoach|plan|phuong\s+an|lo\s+trinh|roadmap)\b[^.,;\n]{0,40}\b(?:khong|chua|khong\s+can)\s+(?:implement|code|thuc\s+hien|thuc\s+thi|sua|lam|build|viet)\b/.test(trimmed)
|
|
2180
|
+
|| (/\b(?:khong|chua|dung)\s+(?:implement|code|thuc\s+hien|thuc\s+thi|sua|lam|viet\s+code)\b/.test(trimmed)
|
|
2181
|
+
&& /\b(?:plan|ke\s+hoach|phuong\s+an|roadmap)\b/.test(trimmed));
|
|
2182
|
+
if (!planSignal) return false;
|
|
2183
|
+
return !hasMutationOrder({ promptText, commandText });
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2160
2186
|
function deriveExecutionMode({
|
|
2161
2187
|
promptText = '',
|
|
2162
2188
|
commandText = '',
|
|
@@ -2844,7 +2870,9 @@ setTimeout(() => process.exit(0), HOOK_DEADLINE_MS).unref();
|
|
|
2844
2870
|
...(typeof routingContext.mutationOrder === 'boolean'
|
|
2845
2871
|
? { mutationOrder: routingContext.mutationOrder }
|
|
2846
2872
|
: {}),
|
|
2847
|
-
|
|
2873
|
+
...(typeof routingContext.planOnly === 'boolean'
|
|
2874
|
+
? { planOnly: routingContext.planOnly }
|
|
2875
|
+
: {}),
|
|
2848
2876
|
}
|
|
2849
2877
|
|
|
2850
2878
|
function compactHelpers({
|
|
@@ -3518,6 +3546,7 @@ setTimeout(() => process.exit(0), HOOK_DEADLINE_MS).unref();
|
|
|
3518
3546
|
// mutation so the Stop gate can release a misrouted write-debt instead
|
|
3519
3547
|
// of looping "make an Edit" on an answer-only question.
|
|
3520
3548
|
mutationOrder: promptText.trim() ? hasMutationOrder({ promptText, commandText }) : null,
|
|
3549
|
+
planOnly: promptText.trim() ? isPlanOnlyRequest({ promptText, commandText }) : null,
|
|
3521
3550
|
};
|
|
3522
3551
|
const previousContext = await buildPreviousContextSnapshot({
|
|
3523
3552
|
projectRoot,
|
|
@@ -3615,6 +3644,9 @@ setTimeout(() => process.exit(0), HOOK_DEADLINE_MS).unref();
|
|
|
3615
3644
|
// Was this prompt a real mutation order? The Stop gate reads this to
|
|
3616
3645
|
// release read-only stops that were misrouted into a write-debt mode.
|
|
3617
3646
|
mutationOrder: hasMutationOrder({ promptText, commandText }),
|
|
3647
|
+
// User-requested default-answer posture: a prompt that only wants a
|
|
3648
|
+
// plan/answer releases the gate even on a mutating route.
|
|
3649
|
+
planOnly: isPlanOnlyRequest({ promptText, commandText }),
|
|
3618
3650
|
};
|
|
3619
3651
|
const useIndexedContext = shouldUseIndexedContext({
|
|
3620
3652
|
activeSkills: selected,
|
|
@@ -326,12 +326,15 @@ function hasUnfinishedCompletion(state = {}, ledger = {}) {
|
|
|
326
326
|
if (!IMPLEMENT_MODES.has(mode)) return false;
|
|
327
327
|
const required = requiredEvidence(state);
|
|
328
328
|
if (required.length === 0) return false;
|
|
329
|
-
//
|
|
330
|
-
//
|
|
331
|
-
// unfinished
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
329
|
+
// USER-REPORTED (2026-10-01): default posture is answer/plan/review — a
|
|
330
|
+
// prompt that never ordered a mutation, or an explicit plan-only request,
|
|
331
|
+
// must not mint unfinished write/verification debt even when it was
|
|
332
|
+
// misrouted into a mutating mode (see evaluateCompletion's
|
|
333
|
+
// 'no-mutation-order' valve). A real edit attempt keeps the debt.
|
|
334
|
+
const readOnlyPrompt = state?.routingContext?.mutationOrder === false
|
|
335
|
+
|| state?.routingContext?.planOnly === true;
|
|
336
|
+
const effectiveRequired = readOnlyPrompt && ledger?.writeAttempted !== true
|
|
337
|
+
? required.filter((item) => item !== 'write-evidence' && item !== 'verification-evidence')
|
|
335
338
|
: required;
|
|
336
339
|
const unfinished = effectiveRequired.some((item) => !evidenceSatisfied(item, ledger, state));
|
|
337
340
|
if (unfinished) return true;
|
|
@@ -2637,30 +2640,29 @@ export function evaluateCompletion({ state = {}, ledger = {}, cwd } = {}) {
|
|
|
2637
2640
|
};
|
|
2638
2641
|
}
|
|
2639
2642
|
|
|
2640
|
-
// USER-REPORTED (2026-10-01):
|
|
2641
|
-
//
|
|
2642
|
-
//
|
|
2643
|
-
//
|
|
2644
|
-
//
|
|
2645
|
-
//
|
|
2646
|
-
//
|
|
2647
|
-
//
|
|
2648
|
-
//
|
|
2649
|
-
//
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
const mutationOrder = state?.routingContext?.mutationOrder;
|
|
2643
|
+
// USER-REPORTED (2026-10-01): the default posture is answer/plan/review —
|
|
2644
|
+
// "chỉ lên plan", "trả lời cho tôi", "xem lại plan" carry no mutation order
|
|
2645
|
+
// and the turn's deliverable is the reply itself. Two stamped signals cover
|
|
2646
|
+
// it: routingContext.mutationOrder === false (no imperative verb survived
|
|
2647
|
+
// the clause strips) or routingContext.planOnly === true (explicit
|
|
2648
|
+
// plan-request wording). When either holds, write-evidence AND
|
|
2649
|
+
// verification-evidence debt from a misrouted mutating lane are
|
|
2650
|
+
// misroutes, not unfinished work — release loud so the stop names why it
|
|
2651
|
+
// is allowed. `null`/absent stays conservative; an actual edit attempt
|
|
2652
|
+
// (writeAttempted) keeps the normal gate regardless of prompt wording.
|
|
2653
|
+
const readOnlyPrompt = state?.routingContext?.mutationOrder === false
|
|
2654
|
+
|| state?.routingContext?.planOnly === true;
|
|
2653
2655
|
if (
|
|
2654
|
-
|
|
2655
|
-
&& missingEvidence.includes('write-evidence')
|
|
2656
|
+
readOnlyPrompt
|
|
2656
2657
|
&& !effectiveLedger.writeAttempted
|
|
2658
|
+
&& missingEvidence.some((item) => item === 'write-evidence' || item === 'verification-evidence')
|
|
2657
2659
|
) {
|
|
2658
2660
|
return {
|
|
2659
2661
|
continue: false,
|
|
2660
2662
|
notify: true,
|
|
2661
2663
|
gateRelease: 'no-mutation-order',
|
|
2662
2664
|
missingEvidence,
|
|
2663
|
-
reason: 'UKit completion gate released a read-only stop: this prompt
|
|
2665
|
+
reason: 'UKit completion gate released a read-only stop: this prompt asked for an answer or a plan, not a change, so missing write/verification evidence is a misroute, not unfinished work. To implement, re-send the task as an explicit change request ("implement", "sửa", "làm luôn", "execute this plan").',
|
|
2664
2666
|
};
|
|
2665
2667
|
}
|
|
2666
2668
|
|