@shahimat/ai-core 0.1.0 → 0.1.1
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 +11 -0
- package/README.md +7 -3
- package/dist/commands/delta/delta.d.ts +1 -0
- package/dist/commands/delta/delta.js +45 -4
- package/dist/commands/delta/index.js +18 -11
- package/dist/registry.js +1 -0
- package/dist/shared/report.d.ts +1 -0
- package/dist/shared/report.js +4 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Свежее сверху. Одна версия — список новых команд и проверок.
|
|
4
4
|
|
|
5
|
+
## 0.1.1
|
|
6
|
+
|
|
7
|
+
- Дельта, попавшая в коммит после сбоя сессии, закрывается: пункт
|
|
8
|
+
засчитывается, если цель изменена в коммитах с появления дельты;
|
|
9
|
+
`delta check` предупреждает о закоммиченной дельте.
|
|
10
|
+
- `delta check --name` и `delta close --name`: нарушения в чужих дельтах
|
|
11
|
+
— предупреждение со списком файлов, а не провал.
|
|
12
|
+
- Дельта не прошла схему — ошибка схемы идёт первой, производные
|
|
13
|
+
нарушения «без revise» по её целям свёрнуты в одну строку.
|
|
14
|
+
- peer `vitest` — `>=3`: проекты на vitest 3 ставятся без ERESOLVE.
|
|
15
|
+
|
|
5
16
|
## 0.1.0
|
|
6
17
|
|
|
7
18
|
- `delta close --name <дельта>` закрывает только эту дельту: чужая
|
package/README.md
CHANGED
|
@@ -89,9 +89,13 @@ Mutation — `context index`, `adapters sync`, `delta close`,
|
|
|
89
89
|
- `ai delta check | close` — дельта проектной документации в
|
|
90
90
|
`context/.delta/*.yml` (пункты `add` / `refine` / `revise`). `check`
|
|
91
91
|
сверяет слои документации с `HEAD`: удалённое или изменённое знание
|
|
92
|
-
без пункта `revise` —
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
без пункта `revise` — нарушение; дельта не прошла схему — её ошибка
|
|
93
|
+
первой, производные нарушения по её целям свёрнуты. `close` после
|
|
94
|
+
приёмки (check чист, пункты выполнены) удаляет дельту и печатает
|
|
95
|
+
строки `revise` для тела коммита. `--name <дельта>` сужает обе
|
|
96
|
+
команды до одной дельты: нарушения чужих — предупреждение. Дельта,
|
|
97
|
+
попавшая в коммит, — предупреждение; её пункт засчитывается, если цель
|
|
98
|
+
изменена в коммитах с появления дельты.
|
|
95
99
|
- `ai role run <name> --file <задание> [--out <ответ>]` — роль из
|
|
96
100
|
`.agents/agents/<name>.md` (в источнике — `agents/<name>.md`)
|
|
97
101
|
headless: текст задания — на stdin, итог — сообщением протокола в
|
|
@@ -137,7 +137,13 @@ function isDeltaBody(v) {
|
|
|
137
137
|
Array.isArray(v['items']) &&
|
|
138
138
|
v['items'].every(isDeltaItem));
|
|
139
139
|
}
|
|
140
|
-
|
|
140
|
+
function targetsOf(data) {
|
|
141
|
+
const items = isRecord(data) ? data['items'] : undefined;
|
|
142
|
+
if (!Array.isArray(items))
|
|
143
|
+
return [];
|
|
144
|
+
return items.flatMap((i) => isRecord(i) && typeof i['target'] === 'string' ? [i['target']] : []);
|
|
145
|
+
}
|
|
146
|
+
async function readDeltas(root, diagnostics, broken) {
|
|
141
147
|
let names = [];
|
|
142
148
|
try {
|
|
143
149
|
names = (await readdir(path.join(root, DELTA_DIR))).filter((f) => /\.ya?ml$/.test(f)).sort();
|
|
@@ -165,6 +171,7 @@ async function readDeltas(root, diagnostics) {
|
|
|
165
171
|
if (issues.length > 0) {
|
|
166
172
|
for (const i of issues)
|
|
167
173
|
diagnostics.push({ file, message: `${i.path || '/'} ${i.message}`.trim() });
|
|
174
|
+
broken.push({ file, targets: targetsOf(data) });
|
|
168
175
|
continue;
|
|
169
176
|
}
|
|
170
177
|
if (!isDeltaBody(data))
|
|
@@ -214,7 +221,8 @@ export async function checkDelta(root) {
|
|
|
214
221
|
if (!git(root, ['rev-parse', '--verify', 'HEAD']).ok)
|
|
215
222
|
throw new UsageError('delta: нужен git-репозиторий с коммитом HEAD');
|
|
216
223
|
const diagnostics = [];
|
|
217
|
-
const
|
|
224
|
+
const broken = [];
|
|
225
|
+
const deltas = await readDeltas(root, diagnostics, broken);
|
|
218
226
|
const roots = await docRoots(root);
|
|
219
227
|
const status = git(root, ['diff', '--name-status', '--no-renames', 'HEAD', '--', ...roots]).out;
|
|
220
228
|
const untracked = git(root, ['ls-files', '--others', '--exclude-standard', '--', ...roots]).out;
|
|
@@ -241,9 +249,15 @@ export async function checkDelta(root) {
|
|
|
241
249
|
...deltas.flatMap((d) => d.items.filter((i) => i.op === 'revise')).map((i) => i.target),
|
|
242
250
|
...accepted.map((a) => a.target),
|
|
243
251
|
];
|
|
252
|
+
const hidden = new Map();
|
|
244
253
|
for (const r of revisions) {
|
|
245
254
|
if (revise.some((t) => matchesTarget(t, r.file)))
|
|
246
255
|
continue;
|
|
256
|
+
const cause = broken.find((b) => b.targets.some((t) => matchesTarget(t, r.file)));
|
|
257
|
+
if (cause !== undefined) {
|
|
258
|
+
hidden.set(cause.file, (hidden.get(cause.file) ?? 0) + 1);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
247
261
|
diagnostics.push({
|
|
248
262
|
file: r.file,
|
|
249
263
|
message: r.deleted
|
|
@@ -251,12 +265,39 @@ export async function checkDelta(root) {
|
|
|
251
265
|
: `изменено существующее знание без пункта revise в дельте: ${r.lost.join('; ')}`,
|
|
252
266
|
});
|
|
253
267
|
}
|
|
254
|
-
|
|
268
|
+
for (const [file, n] of hidden)
|
|
269
|
+
diagnostics.push({
|
|
270
|
+
file,
|
|
271
|
+
message: `дельта не прошла схему — нарушения «без revise» по её целям (${n}) скрыты как следствие; исправь схему`,
|
|
272
|
+
});
|
|
273
|
+
const warnings = committedDeltas(root).map((file) => ({
|
|
274
|
+
file,
|
|
275
|
+
message: `дельта попала в коммит — закрой её: ai delta close --name ${deltaName(file)}; пункты, выполненные в коммитах после её появления, засчитываются`,
|
|
276
|
+
}));
|
|
277
|
+
return { deltas, revisions, changed, diagnostics, warnings, accepted };
|
|
278
|
+
}
|
|
279
|
+
const deltaName = (file) => path.posix.basename(file).replace(/\.ya?ml$/, '');
|
|
280
|
+
function committedDeltas(root) {
|
|
281
|
+
return git(root, ['ls-files', '--', DELTA_DIR])
|
|
282
|
+
.out.split('\n')
|
|
283
|
+
.filter((f) => /\.ya?ml$/.test(f));
|
|
284
|
+
}
|
|
285
|
+
function committedSince(root, file) {
|
|
286
|
+
const added = git(root, ['log', '--diff-filter=A', '--format=%H', '-1', '--', file]).out.trim();
|
|
287
|
+
if (added === '')
|
|
288
|
+
return [];
|
|
289
|
+
const range = git(root, ['rev-parse', '--verify', '-q', `${added}^`]).ok
|
|
290
|
+
? `${added}^..HEAD`
|
|
291
|
+
: 'HEAD';
|
|
292
|
+
return git(root, ['log', '--format=', '--name-only', '--no-renames', range])
|
|
293
|
+
.out.split('\n')
|
|
294
|
+
.filter((f) => f !== '' && isDoc(f));
|
|
255
295
|
}
|
|
256
296
|
export function unfinishedItems(root, result, deltas = result.deltas) {
|
|
257
297
|
const out = [];
|
|
258
|
-
const touched = [...result.changed, ...result.revisions.map((r) => r.file)];
|
|
259
298
|
for (const d of deltas) {
|
|
299
|
+
const committed = committedSince(root, d.file);
|
|
300
|
+
const touched = [...result.changed, ...result.revisions.map((r) => r.file), ...committed];
|
|
260
301
|
d.items.forEach((item, i) => {
|
|
261
302
|
const done = isDeltaTarget(item.target) ||
|
|
262
303
|
(item.op === 'add'
|
|
@@ -1,25 +1,32 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
1
2
|
import { mkdir, rm, writeFile } from 'node:fs/promises';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { ACCEPTED_FILE, acceptedOf, checkDelta, matchesTarget, revisionLines, unfinishedItems, } from './delta.js';
|
|
4
5
|
import { DELTA_DIR } from '#shared/layout';
|
|
5
6
|
import { UsageError } from '#shared/diagnostics';
|
|
6
|
-
import { report } from '#shared/report';
|
|
7
|
+
import { report, warn } from '#shared/report';
|
|
7
8
|
export async function runDelta(command, args) {
|
|
8
9
|
if (command !== 'check' && command !== 'close')
|
|
9
10
|
throw new UsageError(`неизвестная команда: delta ${command ?? ''}`.trimEnd());
|
|
10
11
|
const result = await checkDelta(args.root);
|
|
11
|
-
|
|
12
|
+
const ownFile = args.name === undefined ? undefined : `${DELTA_DIR}/${args.name}.yml`;
|
|
13
|
+
const isForeign = (d) => ownFile !== undefined && d.file.startsWith(`${DELTA_DIR}/`) && d.file !== ownFile;
|
|
14
|
+
const foreign = result.diagnostics.filter(isForeign);
|
|
15
|
+
const errors = result.diagnostics.filter((d) => !isForeign(d));
|
|
16
|
+
warn(result.warnings);
|
|
17
|
+
if (foreign.length > 0) {
|
|
18
|
+
warn(foreign);
|
|
19
|
+
const files = new Set(foreign.map((d) => d.file)).size;
|
|
20
|
+
console.error(`дельты прошлых сессий с нарушениями (${files}): исправь их или удали, если их сессии не продолжатся — сессию ${args.name ?? ''} они не валят`);
|
|
21
|
+
}
|
|
22
|
+
const own = ownFile === undefined ? result.deltas : result.deltas.filter((d) => d.file === ownFile);
|
|
23
|
+
for (const line of revisionLines(own))
|
|
12
24
|
console.log(line);
|
|
13
25
|
if (command === 'check')
|
|
14
|
-
return report('delta check',
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (args.name !== undefined && own.length === 0)
|
|
19
|
-
return report('delta close', [
|
|
20
|
-
{ file: `${DELTA_DIR}/${args.name}.yml`, message: 'дельты нет — закрывать нечего' },
|
|
21
|
-
]);
|
|
22
|
-
const diagnostics = [...result.diagnostics, ...unfinishedItems(args.root, result, own)];
|
|
26
|
+
return report('delta check', errors);
|
|
27
|
+
if (ownFile !== undefined && own.length === 0 && !existsSync(path.join(args.root, ownFile)))
|
|
28
|
+
return report('delta close', [{ file: ownFile, message: 'дельты нет — закрывать нечего' }]);
|
|
29
|
+
const diagnostics = [...errors, ...unfinishedItems(args.root, result, own)];
|
|
23
30
|
if (diagnostics.length > 0)
|
|
24
31
|
return report('delta close', diagnostics);
|
|
25
32
|
const still = result.revisions.map((r) => r.file);
|
package/dist/registry.js
CHANGED
package/dist/shared/report.d.ts
CHANGED
package/dist/shared/report.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shahimat/ai-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Команды для проектов на контрактах ai-prompts: генераторы, проверки, механизм ожиданий и роли",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Gevorg Nakhapetyan",
|
|
@@ -65,14 +65,14 @@
|
|
|
65
65
|
"@types/node": "^24.19.2",
|
|
66
66
|
"typescript": "^7.0.2",
|
|
67
67
|
"vitest": "^5.0.4",
|
|
68
|
-
"@shahimat/cli": "0.1.
|
|
68
|
+
"@shahimat/cli": "0.1.1"
|
|
69
69
|
},
|
|
70
70
|
"publishConfig": {
|
|
71
71
|
"access": "public",
|
|
72
72
|
"registry": "https://registry.npmjs.org/"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
|
-
"vitest": ">=
|
|
75
|
+
"vitest": ">=3"
|
|
76
76
|
},
|
|
77
77
|
"peerDependenciesMeta": {
|
|
78
78
|
"vitest": {
|