@yegor256/jinka 0.0.10 → 0.0.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/bin/jinka.js +2 -1
- package/package.json +1 -1
- package/plugin/skills/clean-up-backlog/SKILL.md +1 -4
- package/src/disk.js +29 -15
- package/src/genie.js +1 -1
- package/src/pile.js +8 -0
- package/src/status.js +2 -2
- package/src/wishes.js +3 -3
package/bin/jinka.js
CHANGED
|
@@ -17,6 +17,7 @@ import { disk } from '../src/disk.js';
|
|
|
17
17
|
import { tty } from '../src/tty.js';
|
|
18
18
|
import { colorless } from '../src/colorless.js';
|
|
19
19
|
import { log } from '../src/log.js';
|
|
20
|
+
import { pile } from '../src/pile.js';
|
|
20
21
|
import { status } from '../src/status.js';
|
|
21
22
|
import { quota } from '../src/quota.js';
|
|
22
23
|
import { rollouts } from '../src/rollouts.js';
|
|
@@ -113,7 +114,7 @@ const screen = summarized(
|
|
|
113
114
|
? tty(
|
|
114
115
|
values.color && !process.env.NO_COLOR ? process.stdout : colorless(process.stdout),
|
|
115
116
|
c,
|
|
116
|
-
status(start, j, a, values.model, t, q),
|
|
117
|
+
status(start, pile(dir), j, a, values.model, t, q),
|
|
117
118
|
legend(f, r)
|
|
118
119
|
)
|
|
119
120
|
: log(process.stdout),
|
package/package.json
CHANGED
|
@@ -9,8 +9,7 @@ description: |
|
|
|
9
9
|
Picks the oldest untouched items, skips those that already have a wish,
|
|
10
10
|
wait for their author, or were triaged within the last day,
|
|
11
11
|
skips pull requests whose latest review requests changes,
|
|
12
|
-
and writes
|
|
13
|
-
one per item, into the wishes directory.
|
|
12
|
+
and writes one new wish per item into the wishes directory.
|
|
14
13
|
---
|
|
15
14
|
|
|
16
15
|
Target every repository named by the wish.
|
|
@@ -31,8 +30,6 @@ Skip issues triaged less than one day ago,
|
|
|
31
30
|
according to `<owner>/<repo>/triaged.csv`
|
|
32
31
|
in the memory directory given in the prompt.
|
|
33
32
|
|
|
34
|
-
Write at most as many new wishes per run as jinka runs threads,
|
|
35
|
-
as the prompt says, across all repositories.
|
|
36
33
|
Write no wish when every candidate was skipped.
|
|
37
34
|
|
|
38
35
|
Write one Markdown file per item, in the root of the wishes directory.
|
package/src/disk.js
CHANGED
|
@@ -2,24 +2,38 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
|
|
4
4
|
import { createWriteStream } from 'node:fs';
|
|
5
|
-
import { mkdir } from 'node:fs/promises';
|
|
5
|
+
import { mkdir, readdir } from 'node:fs/promises';
|
|
6
6
|
import { join, dirname, extname } from 'node:path';
|
|
7
7
|
|
|
8
8
|
export const disk = (wishes, logs, memory, crew) => Object.freeze({
|
|
9
|
-
lines: (wish) =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
9
|
+
lines: async (wish) => {
|
|
10
|
+
const count = (await readdir(wishes, { recursive: true, withFileTypes: true }))
|
|
11
|
+
.filter((f) => f.isFile() && f.name.endsWith('.md'))
|
|
12
|
+
.length;
|
|
13
|
+
return [
|
|
14
|
+
`The wish is written in the file ${join(wishes, wish.name())}, ${wish.fate()}.`,
|
|
15
|
+
'Never delete, move, or rename the file of this wish yourself, even after you split it into smaller wishes, since Jinka takes care of it.',
|
|
16
|
+
`All wishes live in the directory ${wishes}, and you may add new wishes there as Markdown files.`,
|
|
17
|
+
`If this wish is an atomic job that involves no other jobs, make it come true yourself;`,
|
|
18
|
+
`otherwise, do not do it yourself, but split it into smaller wishes`,
|
|
19
|
+
`and write each of them as a separate Markdown file in the directory ${join(wishes, dirname(wish.name()))}.`,
|
|
20
|
+
`Always start the name of every new wish file with a small English letter, never with a capital one.`,
|
|
21
|
+
'Write every new wish the way a person would ask for something, in a few plain sentences that say what must be achieved and give exact links to the issue, pull request, or repository.',
|
|
22
|
+
'Keep every new wish shorter than 1000 characters.',
|
|
23
|
+
'Never copy the text of the current wish, or of these instructions, into a new wish, since Jinka gives these instructions to every wish anyway;',
|
|
24
|
+
'copy only the facts that the new wish cannot find on its own, such as a chat ID or a token that the current wish explicitly asks to pass along.',
|
|
25
|
+
'Leave evidence, observations, logs, and the history of your work out of a new wish, since the new wish checks the current state by itself.',
|
|
26
|
+
`Write at most ${crew.size()} new wishes while doing this wish;`,
|
|
27
|
+
'if more work remains than they can hold, make one of them a continuation that splits the rest later.',
|
|
28
|
+
`The directory ${wishes} holds ${count} wishes right now.`,
|
|
29
|
+
...(count >= 100 ? ['Since that is 100 or more, never split this wish into new wishes, but do all of it yourself, and write no other new wish either, not even to hand over waiting.'] : []),
|
|
30
|
+
`In every Git repository you touch, do all your work inside a separate Git worktree, never in the main directory of the repository, so that other wishes running at the same time do not see your changes.`,
|
|
31
|
+
`Never spend time waiting while doing a wish, neither sleeping nor polling for a build, a review, a merge, or anything else;`,
|
|
32
|
+
`if something must happen first, write a new wish that waits for it and finishes the job, and then complete the current wish right away.`,
|
|
33
|
+
`Your memory lives in the directory ${memory}, keep there what the skills tell you to keep.`,
|
|
34
|
+
`Jinka runs ${crew.size()} threads right now, so up to ${crew.size()} wishes run at once.`
|
|
35
|
+
];
|
|
36
|
+
},
|
|
23
37
|
args: () => ['--add-dir', wishes, '--add-dir', memory],
|
|
24
38
|
log: async (wish) => {
|
|
25
39
|
const file = join(
|
package/src/genie.js
CHANGED
|
@@ -12,7 +12,7 @@ export const genie = (engine, disk, meter, model) => Object.freeze({
|
|
|
12
12
|
'',
|
|
13
13
|
await wish.text(),
|
|
14
14
|
'',
|
|
15
|
-
...disk.lines(wish),
|
|
15
|
+
...(await disk.lines(wish)),
|
|
16
16
|
...engine.lines(),
|
|
17
17
|
'When you finish, end with a single plain sentence that says what you did.',
|
|
18
18
|
'On the next line, write the full URL of the GitHub issue or pull request you worked on, if there was one.'
|
package/src/pile.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright (c) 2026 Yegor Bugayenko
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import { readdirSync } from 'node:fs';
|
|
5
|
+
|
|
6
|
+
export const pile = (dir) => Object.freeze({
|
|
7
|
+
text: () => `${readdirSync(dir, { recursive: true, withFileTypes: true }).filter((f) => f.isFile() && f.name.endsWith('.md')).length}`
|
|
8
|
+
});
|
package/src/status.js
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
import { styleText } from 'node:util';
|
|
5
5
|
import { duration } from './duration.js';
|
|
6
6
|
|
|
7
|
-
export const status = (start, jobs, agent, model, tokens, quota) => Object.freeze({
|
|
7
|
+
export const status = (start, pile, jobs, agent, model, tokens, quota) => Object.freeze({
|
|
8
8
|
text: () => {
|
|
9
9
|
const now = Date.now();
|
|
10
10
|
return [
|
|
11
11
|
`up:${styleText('bgGray', duration(now - start).hours(), { validateStream: false })}`,
|
|
12
|
-
`
|
|
12
|
+
`wishes:${styleText('bgGray', `${pile.text()}/${jobs.text()}`, { validateStream: false })}`,
|
|
13
13
|
`agent:${styleText('bgGray', agent, { validateStream: false })}`,
|
|
14
14
|
`model:${styleText('bgGray', model === '' ? 'default' : model, { validateStream: false })}`,
|
|
15
15
|
`tokens:${styleText('bgGray', tokens.text(), { validateStream: false })}`,
|
package/src/wishes.js
CHANGED
|
@@ -15,8 +15,7 @@ export const wishes = (dir, interval, archive = '', pause = 0, notice = { warn:
|
|
|
15
15
|
signal.throwIfAborted();
|
|
16
16
|
const files = (await readdir(dir, { recursive: true, withFileTypes: true }))
|
|
17
17
|
.filter((f) => f.isFile() && f.name.endsWith('.md'))
|
|
18
|
-
.map((f) => join(relative(dir, f.parentPath), f.name))
|
|
19
|
-
.sort();
|
|
18
|
+
.map((f) => join(relative(dir, f.parentPath), f.name));
|
|
20
19
|
signal.throwIfAborted();
|
|
21
20
|
if (startup.length === 0) {
|
|
22
21
|
startup.push(new Set(files));
|
|
@@ -25,7 +24,8 @@ export const wishes = (dir, interval, archive = '', pause = 0, notice = { warn:
|
|
|
25
24
|
notice.warn(`found ${count} pre-existing ${count === 1 ? 'wish' : 'wishes'} in ${dir} that ${count === 1 ? 'is' : 'are'} not permanent`);
|
|
26
25
|
}
|
|
27
26
|
}
|
|
28
|
-
const
|
|
27
|
+
const fresh = files.filter((f) => !claims.has(f));
|
|
28
|
+
const path = fresh[Math.floor(Math.random() * fresh.length)]
|
|
29
29
|
?? [...claims.keys()].find((c) => !claims.get(c) && files.includes(c));
|
|
30
30
|
if (path === undefined) {
|
|
31
31
|
await setTimeout(interval, undefined, { signal });
|