kenaz 0.3.2 → 0.3.5
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/core/.claude/commands/Ansuz.md +36 -35
- package/core/.claude/hooks/__tests__/task-2395-project-registration.test.js +84 -0
- package/core/.claude/hooks/_ansuz-conversation.js +1 -0
- package/core/.claude/hooks/_ansuz-conversation.test.js +6 -0
- package/core/.claude/hooks/_run-user-prompt.js +0 -4
- package/core/.claude/hooks/_run-user-prompt.test.js +2 -3
- package/core/.claude/hooks/handoff-staleness.js +2 -2
- package/core/.claude/hooks/turn-context.js +50 -2
- package/core/.claude/settings.json +0 -37
- package/core/.collaboration/core/task-api.js +4 -12
- package/core/.formation/README.md +21 -146
- package/core/.formation/bifrost/codex-doctor.js +37 -2
- package/core/.formation/bifrost/codex-doctor.test.js +18 -0
- package/core/.formation/bifrost/launch.js +1 -1
- package/core/.formation/bifrost/openai/codex-hook-shim.js +6 -0
- package/core/.formation/bifrost/openai/codex-hook-shim.test.js +11 -1
- package/core/.formation/bifrost/setup-codex.js +3 -0
- package/core/.formation/spec/task_operations.parity.test.js +0 -1
- package/core/.formation/tools/dashboard-models-chain.test.js +0 -6
- package/core/.formation/tools/dispatch-detail/cross-project-worktree.md +1 -9
- package/core/.formation/tools/dispatch-detail/worktree-subagent-mode.md +2 -2
- package/core/.formation/tools/dispatch.js +28 -549
- package/core/.formation/tools/mcp-allowlist.test.js +0 -1
- package/core/.formation/tools/post-complete/task2345.test.js +0 -5
- package/core/.formation/tools/pre-dispatch-snapshot.test.js +0 -100
- package/core/.formation/tools/prompt.js +66 -1607
- package/core/.formation/tools/prompt.test.js +43 -582
- package/core/.kenaz/claude-hooks-manifest.json +16 -16
- package/core/.specialist/Ansuz/RULES.md +6 -10
- package/core/.specialist/tools/__tests__/freya-cli-contract.test.js +0 -1
- package/core/.specialist/tools/__tests__/task-2218-boot-session-id.test.js +17 -1
- package/core/.specialist/tools/__tests__/task-2222-session-identity-interleave.test.js +17 -1
- package/core/.specialist/tools/__tests__/task-2254-handoff-archive-contract.test.js +6 -5
- package/core/.specialist/tools/ansuz-boot-fast.js +44 -100
- package/core/.specialist/tools/ansuz-boot-fast.test.js +45 -1
- package/core/KENAZ_CORE_VERSION +1 -1
- package/core/dev/plans/ANSUZ_SLIM_PLAN.md +68 -0
- package/core/plugins/kenaz/commands/Amelia.md +1 -1
- package/core/plugins/kenaz/commands/Ansuz.md +36 -35
- package/core/plugins/kenaz/hooks/hooks.json +1 -5
- package/core/plugins/kenaz/hooks/kenaz-hook.js +0 -4
- package/core/plugins/kenaz/scripts/kenaz-mode.js +11 -1
- package/core/plugins/kenaz/scripts/kenaz.test.js +1 -1
- package/core/plugins/kenaz/scripts/sync.js +6 -2
- package/core/plugins/kenaz/scripts/ygg.js +13 -1
- package/core/plugins/kenaz/skills/product_manager/SKILL.md +12 -12
- package/lib/cli.js +47 -6
- package/lib/cli.test.js +24 -0
- package/lib/dashboard.js +10 -0
- package/package.json +11 -7
- package/vendor/install-kenaz-cli.js +18 -7
- package/core/.formation/tools/broadcast.js +0 -103
- package/core/.formation/tools/dispatch-dryrun.test.js +0 -54
- package/core/.formation/tools/dispatch-notifier.js +0 -84
- package/core/.formation/tools/formations.js +0 -1203
- package/core/.formation/tools/headless-branch-recovery.test.js +0 -64
- package/core/.formation/tools/headless-dryrun.test.js +0 -79
- package/core/.formation/tools/headless-v2.js +0 -659
- package/core/.formation/tools/headless.js +0 -1071
- package/core/.formation/tools/legacy-scheduler.js +0 -1994
- package/core/.formation/tools/logger.js +0 -273
- package/core/.formation/tools/memory-loader.js +0 -422
- package/core/.formation/tools/process-state.js +0 -33
- package/core/.formation/tools/remote-conflict.js +0 -98
- package/core/.formation/tools/runner.js +0 -1332
- package/core/.formation/tools/task-utils.js +0 -344
- package/core/.formation/tools/task2028-headless-worktree.test.js +0 -112
- package/core/.formation/tools/tests/headless-mcp-args-plumbing.test.js +0 -123
- package/core/.formation/tools/worktree-leak.test.js +0 -162
- package/core/.specialist/tools/__tests__/task-1863-boot-lease-reuse.test.js +0 -99
- package/core/.specialist/tools/__tests__/task-1977-banner-cap.test.js +0 -129
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
// TASK_2243: pre-dispatch branch recovery must never move a user's branch or drop edits.
|
|
3
|
-
// All git runs in throwaway repos under os.tmpdir() -- never against the real repo.
|
|
4
|
-
const { test } = require('node:test');
|
|
5
|
-
const assert = require('node:assert/strict');
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const os = require('os');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const { execFileSync } = require('child_process');
|
|
10
|
-
const { recoverStaleDispatchBranch } = require('./headless');
|
|
11
|
-
|
|
12
|
-
function git(cwd, ...args) {
|
|
13
|
-
return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: 'pipe', windowsHide: true }).trim();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// main has a.txt; <branch> has a commit changing a.txt (so a dirty edit conflicts with main).
|
|
17
|
-
function makeRepo(t, branch) {
|
|
18
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'recover-branch-'));
|
|
19
|
-
t.after(() => { try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* best effort */ } });
|
|
20
|
-
git(dir, 'init', '--initial-branch=main');
|
|
21
|
-
git(dir, 'config', 'user.email', 't@example.com');
|
|
22
|
-
git(dir, 'config', 'user.name', 't');
|
|
23
|
-
fs.writeFileSync(path.join(dir, 'a.txt'), 'base\n');
|
|
24
|
-
git(dir, 'add', '.');
|
|
25
|
-
git(dir, 'commit', '-m', 'base');
|
|
26
|
-
git(dir, 'checkout', '-b', branch);
|
|
27
|
-
fs.writeFileSync(path.join(dir, 'a.txt'), 'branch work\n');
|
|
28
|
-
git(dir, 'commit', '-am', 'branch work');
|
|
29
|
-
return dir;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const quiet = { log() {}, lockFile: path.join(os.tmpdir(), 'no-such-dispatch-lock-' + process.pid) };
|
|
33
|
-
const run = (dir) => recoverStaleDispatchBranch(dir, {
|
|
34
|
-
...quiet, currentBranch: git(dir, 'rev-parse', '--abbrev-ref', 'HEAD'), targetBranch: 'main',
|
|
35
|
-
});
|
|
36
|
-
const head = (dir) => git(dir, 'rev-parse', '--abbrev-ref', 'HEAD');
|
|
37
|
-
|
|
38
|
-
test('(a) user branch task/600-foo is left alone', (t) => {
|
|
39
|
-
const dir = makeRepo(t, 'task/600-foo');
|
|
40
|
-
run(dir);
|
|
41
|
-
assert.equal(head(dir), 'task/600-foo');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test('(b) dirty tree on task/task_123 keeps edits and branch', (t) => {
|
|
45
|
-
const dir = makeRepo(t, 'task/task_123');
|
|
46
|
-
fs.writeFileSync(path.join(dir, 'a.txt'), 'uncommitted edit\n');
|
|
47
|
-
run(dir);
|
|
48
|
-
assert.equal(head(dir), 'task/task_123');
|
|
49
|
-
assert.equal(fs.readFileSync(path.join(dir, 'a.txt'), 'utf8'), 'uncommitted edit\n');
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test('(c) control: clean stale task/task_123 is recovered to main', (t) => {
|
|
53
|
-
const dir = makeRepo(t, 'task/task_123');
|
|
54
|
-
run(dir);
|
|
55
|
-
assert.equal(head(dir), 'main');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('live dispatch lock for the same card blocks recovery', (t) => {
|
|
59
|
-
const dir = makeRepo(t, 'task/task_123');
|
|
60
|
-
const lockFile = path.join(dir, 'lock.json');
|
|
61
|
-
fs.writeFileSync(lockFile, JSON.stringify({ taskId: 'TASK_123', pid: null, startedAt: new Date().toISOString() }));
|
|
62
|
-
recoverStaleDispatchBranch(dir, { ...quiet, lockFile, currentBranch: 'task/task_123', targetBranch: 'main' });
|
|
63
|
-
assert.equal(head(dir), 'task/task_123');
|
|
64
|
-
});
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
// TASK_2283: headless.js `--dry-run` must report the unit worktree it WOULD create,
|
|
2
|
-
// not create it (nor take the parallel-count lock / run branch recovery).
|
|
3
|
-
// Drives the real CLI (headless.js) against a disposable temp git repo.
|
|
4
|
-
// Sibling of dispatch-dryrun.test.js (TASK_2274, formation-mode dispatch.js).
|
|
5
|
-
const test = require('node:test');
|
|
6
|
-
const assert = require('node:assert/strict');
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const os = require('os');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const { spawnSync } = require('child_process');
|
|
11
|
-
|
|
12
|
-
const HEADLESS = path.join(__dirname, 'headless.js');
|
|
13
|
-
const GIT = ['-c', 'user.email=t@t', '-c', 'user.name=t'];
|
|
14
|
-
|
|
15
|
-
function mkRepo() {
|
|
16
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), 't2283_'));
|
|
17
|
-
fs.mkdirSync(path.join(root, '.kenaz', 'tasks', 'backlog'), { recursive: true });
|
|
18
|
-
fs.writeFileSync(path.join(root, '.kenaz', 'tasks', 'backlog', 'TASK_9001_probe.yaml'), [
|
|
19
|
-
'id: TASK_9001', 'title: probe', 'description: probe', 'status: backlog',
|
|
20
|
-
'priority: P2', 'scope_paths:', ' - src/x.js', ''].join('\n'));
|
|
21
|
-
fs.writeFileSync(path.join(root, 'README.md'), 'x\n');
|
|
22
|
-
const g = (...a) => spawnSync('git', [...GIT, ...a], { cwd: root, encoding: 'utf8', windowsHide: true });
|
|
23
|
-
g('init', '-q'); g('add', '-A'); g('commit', '-q', '-m', 'init');
|
|
24
|
-
return { root, g };
|
|
25
|
-
}
|
|
26
|
-
function run(root, extra, envOver) {
|
|
27
|
-
const env = { ...process.env, ...(envOver || {}) }; delete env.CLAUDE_PROJECT_DIR;
|
|
28
|
-
return spawnSync(process.execPath, [HEADLESS, '--formation=unit', '--skill=do', '--task=TASK_9001',
|
|
29
|
-
'--project-path', root, ...extra], { encoding: 'utf8', env, windowsHide: true, timeout: 90000 });
|
|
30
|
-
}
|
|
31
|
-
const wtDir = (root) => path.join(root, '.formation', 'worktrees', 'task_task_9001');
|
|
32
|
-
|
|
33
|
-
test('headless --dry-run: no worktree, no task branch, reports intent', () => {
|
|
34
|
-
const { root, g } = mkRepo();
|
|
35
|
-
try {
|
|
36
|
-
const r = run(root, ['--dry-run']);
|
|
37
|
-
const out = (r.stdout || '') + (r.stderr || '');
|
|
38
|
-
assert.equal(r.status, 0, out.slice(-600));
|
|
39
|
-
assert.ok(!fs.existsSync(wtDir(root)), 'dry-run must not create the unit worktree');
|
|
40
|
-
assert.equal(g('branch', '--list', 'task/task_9001').stdout.trim(), '', 'dry-run must not create the task branch');
|
|
41
|
-
assert.match(out, /\[dry-run\] would create unit worktree: .*task_task_9001/);
|
|
42
|
-
assert.match(out, /count lock NOT taken/);
|
|
43
|
-
assert.ok(!/Unit worktree:/.test(out), 'no real worktree creation log');
|
|
44
|
-
// follow-up: no auto-activate (task move + commit) and no .dispatch-branch write
|
|
45
|
-
const taskFile = (d) => path.join(root, '.kenaz', 'tasks', d, 'TASK_9001_probe.yaml');
|
|
46
|
-
assert.ok(fs.existsSync(taskFile('backlog')) && !fs.existsSync(taskFile('active')), 'task status must stay backlog');
|
|
47
|
-
assert.ok(!/Auto-activate/.test(g('log', '--format=%s').stdout), 'no Auto-activate commit');
|
|
48
|
-
assert.equal(g('rev-list', '--count', 'HEAD').stdout.trim(), '1', 'no commits added');
|
|
49
|
-
assert.ok(!fs.existsSync(path.join(root, '.formation', '.dispatch-branch')), 'no .dispatch-branch write');
|
|
50
|
-
assert.match(out, /\[dry-run\] would auto-activate TASK_9001/);
|
|
51
|
-
assert.match(out, /\[dry-run\] would record dispatch branch/);
|
|
52
|
-
} finally { fs.rmSync(root, { recursive: true, force: true }); }
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('control: non-dry-run headless DOES create the unit worktree', () => {
|
|
56
|
-
const { root } = mkRepo();
|
|
57
|
-
// persistDispatchBranch always writes <core ROOT>/.formation/.dispatch-branch, which
|
|
58
|
-
// post-complete getDispatchBranch reads to return to the user's branch. Restore it.
|
|
59
|
-
const realDispatchBranch = path.join(__dirname, '..', '.dispatch-branch');
|
|
60
|
-
const saved = fs.existsSync(realDispatchBranch) ? fs.readFileSync(realDispatchBranch) : null;
|
|
61
|
-
try {
|
|
62
|
-
// PATH = node + git dirs only and a fake HOME/USERPROFILE: the agent launch that
|
|
63
|
-
// follows the worktree step cannot find a real claude binary (it fails after the
|
|
64
|
-
// step we assert on). Never drop this isolation — a probe once launched a real agent.
|
|
65
|
-
const gitDirs = spawnSync(process.platform === 'win32' ? 'where' : 'which', ['git'], { encoding: 'utf8' })
|
|
66
|
-
.stdout.split(/\r?\n/).filter(Boolean).map((p) => path.dirname(p));
|
|
67
|
-
const PATH = [path.dirname(process.execPath), ...gitDirs].join(path.delimiter);
|
|
68
|
-
// Count file redirected to the temp repo (KENAZ_PARALLEL_COUNT_FILE) so a crash cannot
|
|
69
|
-
// leave the real ROOT/.formation/.parallel-count inflated.
|
|
70
|
-
const r = run(root, [], { PATH, Path: PATH, USERPROFILE: root, HOME: root,
|
|
71
|
-
KENAZ_PARALLEL_COUNT_FILE: path.join(root, 'parallel-count') });
|
|
72
|
-
const out = (r.stdout || '') + (r.stderr || '');
|
|
73
|
-
assert.match(out, /Unit worktree: \.formation\/worktrees\/task_task_9001/, 'control: worktree creation expected\n' + out.slice(-800));
|
|
74
|
-
} finally {
|
|
75
|
-
if (saved === null) fs.rmSync(realDispatchBranch, { force: true });
|
|
76
|
-
else fs.writeFileSync(realDispatchBranch, saved);
|
|
77
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
78
|
-
}
|
|
79
|
-
});
|