@protoboxai/codeloop 0.3.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/LICENSE +21 -0
- package/README.md +589 -0
- package/dist/commands/adopt.d.ts +2 -0
- package/dist/commands/adopt.js +26 -0
- package/dist/commands/adopt.js.map +1 -0
- package/dist/commands/brief.d.ts +2 -0
- package/dist/commands/brief.js +22 -0
- package/dist/commands/brief.js.map +1 -0
- package/dist/commands/card.d.ts +6 -0
- package/dist/commands/card.js +159 -0
- package/dist/commands/card.js.map +1 -0
- package/dist/commands/check.d.ts +4 -0
- package/dist/commands/check.js +76 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/cloud.d.ts +8 -0
- package/dist/commands/cloud.js +123 -0
- package/dist/commands/cloud.js.map +1 -0
- package/dist/commands/guard.d.ts +2 -0
- package/dist/commands/guard.js +26 -0
- package/dist/commands/guard.js.map +1 -0
- package/dist/commands/import.d.ts +2 -0
- package/dist/commands/import.js +24 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/inbox.d.ts +2 -0
- package/dist/commands/inbox.js +48 -0
- package/dist/commands/inbox.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +142 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +125 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/lane.d.ts +2 -0
- package/dist/commands/lane.js +100 -0
- package/dist/commands/lane.js.map +1 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.js +35 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +77 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/mock.d.ts +3 -0
- package/dist/commands/mock.js +22 -0
- package/dist/commands/mock.js.map +1 -0
- package/dist/commands/pack.d.ts +2 -0
- package/dist/commands/pack.js +19 -0
- package/dist/commands/pack.js.map +1 -0
- package/dist/commands/publish.d.ts +2 -0
- package/dist/commands/publish.js +125 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/remove.d.ts +2 -0
- package/dist/commands/remove.js +31 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/render.d.ts +5 -0
- package/dist/commands/render.js +47 -0
- package/dist/commands/render.js.map +1 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +45 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/scan.d.ts +2 -0
- package/dist/commands/scan.js +26 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/schedule.d.ts +2 -0
- package/dist/commands/schedule.js +51 -0
- package/dist/commands/schedule.js.map +1 -0
- package/dist/commands/search.d.ts +2 -0
- package/dist/commands/search.js +85 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/serve.d.ts +7 -0
- package/dist/commands/serve.js +126 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/spec.d.ts +3 -0
- package/dist/commands/spec.js +64 -0
- package/dist/commands/spec.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.js +251 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.js +83 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/verify.d.ts +3 -0
- package/dist/commands/verify.js +75 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/watch.d.ts +6 -0
- package/dist/commands/watch.js +111 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/commands/wiki.d.ts +3 -0
- package/dist/commands/wiki.js +85 -0
- package/dist/commands/wiki.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/agent.d.ts +27 -0
- package/dist/lib/agent.js +178 -0
- package/dist/lib/agent.js.map +1 -0
- package/dist/lib/board.d.ts +38 -0
- package/dist/lib/board.js +86 -0
- package/dist/lib/board.js.map +1 -0
- package/dist/lib/cards.d.ts +82 -0
- package/dist/lib/cards.js +107 -0
- package/dist/lib/cards.js.map +1 -0
- package/dist/lib/clock.d.ts +5 -0
- package/dist/lib/clock.js +14 -0
- package/dist/lib/clock.js.map +1 -0
- package/dist/lib/cloud.d.ts +122 -0
- package/dist/lib/cloud.js +580 -0
- package/dist/lib/cloud.js.map +1 -0
- package/dist/lib/competitors.d.ts +28 -0
- package/dist/lib/competitors.js +90 -0
- package/dist/lib/competitors.js.map +1 -0
- package/dist/lib/config.d.ts +7 -0
- package/dist/lib/config.js +25 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/cron.d.ts +5 -0
- package/dist/lib/cron.js +82 -0
- package/dist/lib/cron.js.map +1 -0
- package/dist/lib/detect.d.ts +13 -0
- package/dist/lib/detect.js +60 -0
- package/dist/lib/detect.js.map +1 -0
- package/dist/lib/engine.d.ts +88 -0
- package/dist/lib/engine.js +435 -0
- package/dist/lib/engine.js.map +1 -0
- package/dist/lib/flow.d.ts +44 -0
- package/dist/lib/flow.js +121 -0
- package/dist/lib/flow.js.map +1 -0
- package/dist/lib/glob.d.ts +2 -0
- package/dist/lib/glob.js +10 -0
- package/dist/lib/glob.js.map +1 -0
- package/dist/lib/import.d.ts +13 -0
- package/dist/lib/import.js +127 -0
- package/dist/lib/import.js.map +1 -0
- package/dist/lib/inbox.d.ts +42 -0
- package/dist/lib/inbox.js +65 -0
- package/dist/lib/inbox.js.map +1 -0
- package/dist/lib/lane.d.ts +71 -0
- package/dist/lib/lane.js +105 -0
- package/dist/lib/lane.js.map +1 -0
- package/dist/lib/lock.d.ts +6 -0
- package/dist/lib/lock.js +64 -0
- package/dist/lib/lock.js.map +1 -0
- package/dist/lib/mcp.d.ts +2 -0
- package/dist/lib/mcp.js +57 -0
- package/dist/lib/mcp.js.map +1 -0
- package/dist/lib/mock.d.ts +17 -0
- package/dist/lib/mock.js +137 -0
- package/dist/lib/mock.js.map +1 -0
- package/dist/lib/pack.d.ts +24 -0
- package/dist/lib/pack.js +77 -0
- package/dist/lib/pack.js.map +1 -0
- package/dist/lib/proposals.d.ts +48 -0
- package/dist/lib/proposals.js +240 -0
- package/dist/lib/proposals.js.map +1 -0
- package/dist/lib/render.d.ts +7 -0
- package/dist/lib/render.js +84 -0
- package/dist/lib/render.js.map +1 -0
- package/dist/lib/research.d.ts +8 -0
- package/dist/lib/research.js +39 -0
- package/dist/lib/research.js.map +1 -0
- package/dist/lib/run.d.ts +38 -0
- package/dist/lib/run.js +215 -0
- package/dist/lib/run.js.map +1 -0
- package/dist/lib/scaffold.d.ts +16 -0
- package/dist/lib/scaffold.js +175 -0
- package/dist/lib/scaffold.js.map +1 -0
- package/dist/lib/scan.d.ts +22 -0
- package/dist/lib/scan.js +85 -0
- package/dist/lib/scan.js.map +1 -0
- package/dist/lib/schedule.d.ts +20 -0
- package/dist/lib/schedule.js +66 -0
- package/dist/lib/schedule.js.map +1 -0
- package/dist/lib/server.d.ts +68 -0
- package/dist/lib/server.js +235 -0
- package/dist/lib/server.js.map +1 -0
- package/dist/lib/shell.d.ts +1 -0
- package/dist/lib/shell.js +25 -0
- package/dist/lib/shell.js.map +1 -0
- package/dist/lib/skills.d.ts +20 -0
- package/dist/lib/skills.js +92 -0
- package/dist/lib/skills.js.map +1 -0
- package/dist/lib/spec.d.ts +24 -0
- package/dist/lib/spec.js +102 -0
- package/dist/lib/spec.js.map +1 -0
- package/dist/lib/stats.d.ts +13 -0
- package/dist/lib/stats.js +43 -0
- package/dist/lib/stats.js.map +1 -0
- package/dist/lib/verify.d.ts +25 -0
- package/dist/lib/verify.js +135 -0
- package/dist/lib/verify.js.map +1 -0
- package/dist/lib/version.d.ts +10 -0
- package/dist/lib/version.js +27 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/lib/wiki.d.ts +51 -0
- package/dist/lib/wiki.js +164 -0
- package/dist/lib/wiki.js.map +1 -0
- package/dist/registry/index.d.ts +7 -0
- package/dist/registry/index.js +8 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/installer.d.ts +30 -0
- package/dist/registry/installer.js +133 -0
- package/dist/registry/installer.js.map +1 -0
- package/dist/registry/local-index.d.ts +32 -0
- package/dist/registry/local-index.js +58 -0
- package/dist/registry/local-index.js.map +1 -0
- package/dist/registry/lockfile.d.ts +40 -0
- package/dist/registry/lockfile.js +85 -0
- package/dist/registry/lockfile.js.map +1 -0
- package/dist/registry/security.d.ts +25 -0
- package/dist/registry/security.js +100 -0
- package/dist/registry/security.js.map +1 -0
- package/dist/registry/skill-schema.d.ts +30 -0
- package/dist/registry/skill-schema.js +95 -0
- package/dist/registry/skill-schema.js.map +1 -0
- package/dist/ui/404.html +1 -0
- package/dist/ui/_next/static/6wzGE5sCtbYkSyhMWxL32/_buildManifest.js +1 -0
- package/dist/ui/_next/static/6wzGE5sCtbYkSyhMWxL32/_ssgManifest.js +1 -0
- package/dist/ui/_next/static/chunks/255-54d3085ce94738a4.js +1 -0
- package/dist/ui/_next/static/chunks/423-bb541b7ae2733575.js +1 -0
- package/dist/ui/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +1 -0
- package/dist/ui/_next/static/chunks/app/_not-found/page-d6bc774f7acb716e.js +1 -0
- package/dist/ui/_next/static/chunks/app/layout-e5fc8e78e1c8da95.js +1 -0
- package/dist/ui/_next/static/chunks/app/page-0c34b6e119cef236.js +1 -0
- package/dist/ui/_next/static/chunks/framework-de98b93a850cfc71.js +1 -0
- package/dist/ui/_next/static/chunks/main-49fd204fc9037ea3.js +1 -0
- package/dist/ui/_next/static/chunks/main-app-c46afa2f48f3aaef.js +1 -0
- package/dist/ui/_next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
- package/dist/ui/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
- package/dist/ui/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dist/ui/_next/static/chunks/webpack-4a462cecab786e93.js +1 -0
- package/dist/ui/_next/static/css/1bf01240dbfd6088.css +1 -0
- package/dist/ui/index.html +1 -0
- package/dist/ui/index.txt +19 -0
- package/dist/watch/index.d.ts +21 -0
- package/dist/watch/index.js +88 -0
- package/dist/watch/index.js.map +1 -0
- package/dist/watch/reporter.d.ts +11 -0
- package/dist/watch/reporter.js +44 -0
- package/dist/watch/reporter.js.map +1 -0
- package/dist/watch/signals.d.ts +38 -0
- package/dist/watch/signals.js +119 -0
- package/dist/watch/signals.js.map +1 -0
- package/dist/watch/triggers.d.ts +10 -0
- package/dist/watch/triggers.js +67 -0
- package/dist/watch/triggers.js.map +1 -0
- package/package.json +64 -0
- package/registry/index.json +106 -0
- package/starters/generic.yaml +95 -0
- package/starters/go.yaml +99 -0
- package/starters/node-typescript.yaml +108 -0
- package/starters/python.yaml +105 -0
- package/templates/ci/codeloop-pr.yml +29 -0
- package/templates/ci/codeloop-prod.yml +27 -0
- package/templates/ci/codeloop-staging.yml +34 -0
- package/templates/codeloop/board.json +5 -0
- package/templates/codeloop/gotchas.md +13 -0
- package/templates/codeloop/patterns.md +15 -0
- package/templates/codeloop/principles.md +49 -0
- package/templates/codeloop/rules.md +23 -0
- package/templates/commands/commit.md +255 -0
- package/templates/commands/debug.md +142 -0
- package/templates/commands/deploy.md +144 -0
- package/templates/commands/design.md +102 -0
- package/templates/commands/manage.md +77 -0
- package/templates/commands/plan.md +84 -0
- package/templates/commands/qa.md +155 -0
- package/templates/commands/reflect.md +93 -0
- package/templates/commands/ship.md +187 -0
- package/templates/commands/test.md +133 -0
- package/templates/hooks/commit-msg +8 -0
- package/templates/lanes/analyze.yaml +25 -0
- package/templates/lanes/build.yaml +45 -0
- package/templates/lanes/deploy.yaml +24 -0
- package/templates/lanes/learn.yaml +20 -0
- package/templates/lanes/market.yaml +25 -0
- package/templates/lanes/plan.yaml +24 -0
- package/templates/lanes/scan.yaml +11 -0
- package/templates/lanes/triage.yaml +24 -0
- package/templates/mock/template.html +82 -0
- package/templates/seeds/go-gotchas.md +28 -0
- package/templates/seeds/go-patterns.md +22 -0
- package/templates/seeds/node-typescript-gotchas.md +30 -0
- package/templates/seeds/node-typescript-patterns.md +27 -0
- package/templates/seeds/python-gotchas.md +30 -0
- package/templates/seeds/python-patterns.md +19 -0
- package/templates/seeds/universal-gotchas.md +11 -0
- package/templates/seeds/universal-patterns.md +11 -0
- package/templates/spec/plan.md +7 -0
- package/templates/spec/research.md +19 -0
- package/templates/spec/spec.md +14 -0
- package/templates/spec/tasks.md +10 -0
- package/templates/tasks/todo.md +3 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
const DEFAULT_COLUMNS = ['backlog', 'planned', 'in_progress', 'review', 'done'];
|
|
4
|
+
export function createBoard() {
|
|
5
|
+
return {
|
|
6
|
+
version: 1,
|
|
7
|
+
columns: [...DEFAULT_COLUMNS],
|
|
8
|
+
tasks: [],
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function nextId(board) {
|
|
12
|
+
if (board.tasks.length === 0)
|
|
13
|
+
return 't-001';
|
|
14
|
+
const maxNum = board.tasks.reduce((max, t) => {
|
|
15
|
+
const num = parseInt(t.id.replace('t-', ''), 10);
|
|
16
|
+
return num > max ? num : max;
|
|
17
|
+
}, 0);
|
|
18
|
+
return `t-${String(maxNum + 1).padStart(3, '0')}`;
|
|
19
|
+
}
|
|
20
|
+
export function addTask(board, input) {
|
|
21
|
+
const now = new Date().toISOString();
|
|
22
|
+
const task = {
|
|
23
|
+
id: nextId(board),
|
|
24
|
+
title: input.title,
|
|
25
|
+
description: input.description,
|
|
26
|
+
status: input.status ?? 'backlog',
|
|
27
|
+
labels: input.labels ?? [],
|
|
28
|
+
steps: input.steps ? [...input.steps] : [],
|
|
29
|
+
commits: [],
|
|
30
|
+
acceptanceCriteria: input.acceptanceCriteria,
|
|
31
|
+
createdAt: now,
|
|
32
|
+
updatedAt: now,
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
...board,
|
|
36
|
+
tasks: [...board.tasks, task],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function updateTask(board, id, patch) {
|
|
40
|
+
const index = board.tasks.findIndex(t => t.id === id);
|
|
41
|
+
if (index === -1)
|
|
42
|
+
throw new Error(`Task ${id} not found`);
|
|
43
|
+
const now = new Date().toISOString();
|
|
44
|
+
const updated = {
|
|
45
|
+
...board.tasks[index],
|
|
46
|
+
...patch,
|
|
47
|
+
updatedAt: now,
|
|
48
|
+
};
|
|
49
|
+
const tasks = [...board.tasks];
|
|
50
|
+
tasks[index] = updated;
|
|
51
|
+
return { ...board, tasks };
|
|
52
|
+
}
|
|
53
|
+
export function moveTask(board, id, newStatus) {
|
|
54
|
+
return updateTask(board, id, { status: newStatus });
|
|
55
|
+
}
|
|
56
|
+
export function deleteTask(board, id) {
|
|
57
|
+
const index = board.tasks.findIndex(t => t.id === id);
|
|
58
|
+
if (index === -1)
|
|
59
|
+
throw new Error(`Task ${id} not found`);
|
|
60
|
+
return {
|
|
61
|
+
...board,
|
|
62
|
+
tasks: board.tasks.filter(t => t.id !== id),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function getTask(board, id) {
|
|
66
|
+
return board.tasks.find(t => t.id === id);
|
|
67
|
+
}
|
|
68
|
+
const BOARD_PATH = '.codeloop/board.json';
|
|
69
|
+
export function loadBoard(dir) {
|
|
70
|
+
const filePath = join(dir, BOARD_PATH);
|
|
71
|
+
if (!existsSync(filePath))
|
|
72
|
+
return createBoard();
|
|
73
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
74
|
+
return JSON.parse(content);
|
|
75
|
+
}
|
|
76
|
+
export function saveBoard(dir, board) {
|
|
77
|
+
const filePath = join(dir, BOARD_PATH);
|
|
78
|
+
const dirPath = dirname(filePath);
|
|
79
|
+
if (!existsSync(dirPath)) {
|
|
80
|
+
mkdirSync(dirPath, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
const tmpPath = filePath + '.tmp';
|
|
83
|
+
writeFileSync(tmpPath, JSON.stringify(board, null, 2) + '\n', 'utf-8');
|
|
84
|
+
renameSync(tmpPath, filePath);
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=board.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board.js","sourceRoot":"","sources":["../../src/lib/board.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export interface CardEvent {
|
|
2
|
+
at: string;
|
|
3
|
+
actor: string;
|
|
4
|
+
human: boolean;
|
|
5
|
+
action: string;
|
|
6
|
+
stage?: string;
|
|
7
|
+
note?: string;
|
|
8
|
+
/** On an approval: what the stage's output and check were when it was given. */
|
|
9
|
+
fingerprint?: string;
|
|
10
|
+
/** Reserved for an approval recorded by an authenticated store. Local approvals have none. */
|
|
11
|
+
approvalId?: string;
|
|
12
|
+
/** On agent-start and agent-run: the configured agent, its exit code (null when killed), and where its output went. */
|
|
13
|
+
agent?: string;
|
|
14
|
+
exit?: number | null;
|
|
15
|
+
durationMs?: number;
|
|
16
|
+
log?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface Card {
|
|
19
|
+
id: string;
|
|
20
|
+
title: string;
|
|
21
|
+
lane: string;
|
|
22
|
+
laneVersion: number;
|
|
23
|
+
stage: string;
|
|
24
|
+
/** Folder key when another card already has this number (CL-001 and c-001): the lowercased id. */
|
|
25
|
+
key?: string;
|
|
26
|
+
spec?: string;
|
|
27
|
+
/** On a proposal: what was seen and where, for the owner deciding whether to promote it. */
|
|
28
|
+
description?: string;
|
|
29
|
+
source?: string;
|
|
30
|
+
/** Names what a proposal was made from, so the same finding is never proposed twice. */
|
|
31
|
+
dedupe?: string;
|
|
32
|
+
gate?: string;
|
|
33
|
+
awaiting?: string;
|
|
34
|
+
retries: Record<string, number>;
|
|
35
|
+
/** Per stage: fingerprint of the work at its last failed check, so an unattended run can tell nothing changed. */
|
|
36
|
+
failures?: Record<string, string>;
|
|
37
|
+
evidence: string[];
|
|
38
|
+
events: CardEvent[];
|
|
39
|
+
createdAt: string;
|
|
40
|
+
updatedAt: string;
|
|
41
|
+
}
|
|
42
|
+
export interface CardFile {
|
|
43
|
+
version: number;
|
|
44
|
+
cards: Card[];
|
|
45
|
+
}
|
|
46
|
+
export declare const CARDS_PATH = ".codeloop/cards.json";
|
|
47
|
+
export declare const DONE = "done";
|
|
48
|
+
/** A card someone proposed and the owner has not promoted. It waits at gate `proposal` and holds no place in its lane. */
|
|
49
|
+
export declare const PROPOSED = "proposed";
|
|
50
|
+
export declare const PROPOSAL_GATE = "proposal";
|
|
51
|
+
/** A proposal the owner turned down. */
|
|
52
|
+
export declare const DROPPED = "dropped";
|
|
53
|
+
/** In its lane and not finished. A proposal, promoted or not yet, is neither started nor counted until the owner promotes it. */
|
|
54
|
+
export declare const inLane: (card: {
|
|
55
|
+
stage: string;
|
|
56
|
+
}) => boolean;
|
|
57
|
+
/** A check, gate or permission said no. The CLI maps this to exit code 2. */
|
|
58
|
+
export declare class RefusalError extends Error {
|
|
59
|
+
constructor(message: string);
|
|
60
|
+
}
|
|
61
|
+
/** The name a card's spec, use-case and evidence folders are keyed by: its number, unless that number is shared. */
|
|
62
|
+
export declare function cardKey(card: {
|
|
63
|
+
id: string;
|
|
64
|
+
key?: string;
|
|
65
|
+
}): string;
|
|
66
|
+
/** `findCard`, but undefined when no card has that id or number. An ambiguous number still throws. */
|
|
67
|
+
export declare function findCardOrNone(cards: Card[], ref: string): Card | undefined;
|
|
68
|
+
export declare const CARD_ID: RegExp;
|
|
69
|
+
/** For anything that becomes a file or folder name: one path component, nothing that climbs out. */
|
|
70
|
+
export declare function safeName(value: string, what: string): string;
|
|
71
|
+
/** Accepts the full id in any case, or its number alone: `1`, `001`, `c-001` and `CL-001` can all name one card. */
|
|
72
|
+
export declare function findCard(cards: Card[], ref: string): Card;
|
|
73
|
+
export declare class ConflictError extends Error {
|
|
74
|
+
constructor(message: string);
|
|
75
|
+
}
|
|
76
|
+
export declare function readCards(projectDir: string): CardFile;
|
|
77
|
+
/**
|
|
78
|
+
* Compare-and-swap: `read` is the file as the caller read it. The write is refused when the
|
|
79
|
+
* version on disk has moved, so two agents working from the same read cannot both land.
|
|
80
|
+
*/
|
|
81
|
+
export declare function writeCards(projectDir: string, read: CardFile, cards: Card[]): CardFile;
|
|
82
|
+
export declare function nextCardId(cards: Card[]): string;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { existsSync, readFileSync, renameSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { CloudConflictError, writeCloudBoard } from './cloud.js';
|
|
4
|
+
import { withLock } from './lock.js';
|
|
5
|
+
export const CARDS_PATH = '.codeloop/cards.json';
|
|
6
|
+
export const DONE = 'done';
|
|
7
|
+
/** A card someone proposed and the owner has not promoted. It waits at gate `proposal` and holds no place in its lane. */
|
|
8
|
+
export const PROPOSED = 'proposed';
|
|
9
|
+
export const PROPOSAL_GATE = 'proposal';
|
|
10
|
+
/** A proposal the owner turned down. */
|
|
11
|
+
export const DROPPED = 'dropped';
|
|
12
|
+
/** In its lane and not finished. A proposal, promoted or not yet, is neither started nor counted until the owner promotes it. */
|
|
13
|
+
export const inLane = (card) => card.stage !== DONE && card.stage !== PROPOSED && card.stage !== DROPPED;
|
|
14
|
+
/** A check, gate or permission said no. The CLI maps this to exit code 2. */
|
|
15
|
+
export class RefusalError extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'RefusalError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** The name a card's spec, use-case and evidence folders are keyed by: its number, unless that number is shared. */
|
|
22
|
+
export function cardKey(card) {
|
|
23
|
+
return card.key ?? card.id.replace(/\D/g, '').padStart(3, '0');
|
|
24
|
+
}
|
|
25
|
+
/** `findCard`, but undefined when no card has that id or number. An ambiguous number still throws. */
|
|
26
|
+
export function findCardOrNone(cards, ref) {
|
|
27
|
+
try {
|
|
28
|
+
return findCard(cards, ref);
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
if (e instanceof RefusalError && e.message.includes('not found'))
|
|
32
|
+
return undefined;
|
|
33
|
+
throw e;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export const CARD_ID = /^[A-Za-z]{1,10}-\d{1,6}$/;
|
|
37
|
+
/** For anything that becomes a file or folder name: one path component, nothing that climbs out. */
|
|
38
|
+
export function safeName(value, what) {
|
|
39
|
+
if (typeof value !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value) || value.includes('..')) {
|
|
40
|
+
throw new RefusalError(`${what} "${value}" is not a plain name (letters, digits, dot, dash, underscore)`);
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
/** Accepts the full id in any case, or its number alone: `1`, `001`, `c-001` and `CL-001` can all name one card. */
|
|
45
|
+
export function findCard(cards, ref) {
|
|
46
|
+
const exact = cards.find(c => c.id === ref) ?? cards.find(c => c.id.toLowerCase() === ref.toLowerCase());
|
|
47
|
+
if (exact)
|
|
48
|
+
return exact;
|
|
49
|
+
const digits = ref.replace(/\D/g, '');
|
|
50
|
+
const matches = digits ? cards.filter(c => parseInt(c.id.replace(/\D/g, '') || '-1', 10) === parseInt(digits, 10)) : [];
|
|
51
|
+
// A prefixed ref (c-1) must agree with the card's prefix; a bare number matches any prefix.
|
|
52
|
+
const prefix = ref.replace(/[\d-]+$/, '').toLowerCase();
|
|
53
|
+
const fits = prefix ? matches.filter(c => c.id.replace(/[\d-]+$/, '').toLowerCase() === prefix) : matches;
|
|
54
|
+
if (fits.length === 1)
|
|
55
|
+
return fits[0];
|
|
56
|
+
if (fits.length > 1)
|
|
57
|
+
throw new RefusalError(`"${ref}" matches ${fits.map(c => c.id).join(', ')}; use the full id`);
|
|
58
|
+
throw new RefusalError(`card "${ref}" not found`);
|
|
59
|
+
}
|
|
60
|
+
export class ConflictError extends Error {
|
|
61
|
+
constructor(message) {
|
|
62
|
+
super(message);
|
|
63
|
+
this.name = 'ConflictError';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function readCards(projectDir) {
|
|
67
|
+
const file = join(projectDir, CARDS_PATH);
|
|
68
|
+
if (!existsSync(file))
|
|
69
|
+
return { version: 0, cards: [] };
|
|
70
|
+
return JSON.parse(readFileSync(file, 'utf-8'));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Compare-and-swap: `read` is the file as the caller read it. The write is refused when the
|
|
74
|
+
* version on disk has moved, so two agents working from the same read cannot both land.
|
|
75
|
+
*/
|
|
76
|
+
export function writeCards(projectDir, read, cards) {
|
|
77
|
+
const file = join(projectDir, CARDS_PATH);
|
|
78
|
+
// The lock covers the re-read, the version check and the rename as one step. A connected cloud
|
|
79
|
+
// store is written inside it too, so its revision record cannot be interleaved either.
|
|
80
|
+
return withLock(file, () => {
|
|
81
|
+
const current = readCards(projectDir);
|
|
82
|
+
if (current.version !== read.version) {
|
|
83
|
+
throw new ConflictError(`cards.json changed since it was read (read version ${read.version}, now ${current.version}); re-read and retry`);
|
|
84
|
+
}
|
|
85
|
+
const next = { version: read.version + 1, cards };
|
|
86
|
+
const content = JSON.stringify(next, null, 2) + '\n';
|
|
87
|
+
// The cloud is written first, so a board that moved on elsewhere refuses this write before
|
|
88
|
+
// the local file changes.
|
|
89
|
+
try {
|
|
90
|
+
writeCloudBoard(projectDir, content);
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
if (e instanceof CloudConflictError)
|
|
94
|
+
throw new ConflictError(`the cloud board changed since this checkout last saw it (${e.message}); run \`codeloop cloud pull\` and retry`);
|
|
95
|
+
throw e;
|
|
96
|
+
}
|
|
97
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
98
|
+
writeFileSync(tmp, content);
|
|
99
|
+
renameSync(tmp, file);
|
|
100
|
+
return next;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
export function nextCardId(cards) {
|
|
104
|
+
const max = cards.reduce((m, c) => Math.max(m, parseInt(c.id.replace(/\D/g, '') || '0', 10)), 0);
|
|
105
|
+
return `c-${String(max + 1).padStart(3, '0')}`;
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=cards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cards.js","sourceRoot":"","sources":["../../src/lib/cards.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The current time. CODELOOP_NOW replaces it, so a test or a demonstration can place a command on
|
|
3
|
+
* a chosen day and have every event it writes carry that time. `run --now` does the same for one run.
|
|
4
|
+
*/
|
|
5
|
+
export function clock() {
|
|
6
|
+
const set = process.env.CODELOOP_NOW;
|
|
7
|
+
if (!set)
|
|
8
|
+
return new Date();
|
|
9
|
+
const at = new Date(set);
|
|
10
|
+
if (Number.isNaN(at.getTime()))
|
|
11
|
+
throw new Error(`CODELOOP_NOW "${set}" is not a date`);
|
|
12
|
+
return at;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=clock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clock.js","sourceRoot":"","sources":["../../src/lib/clock.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,KAAK;IACnB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACrC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,IAAI,EAAE,CAAC;IAC5B,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,GAAG,iBAAiB,CAAC,CAAC;IACvF,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
export declare const CLOUD_FILE = ".codeloop/cloud.json";
|
|
2
|
+
export declare const SYNC_FILE = ".codeloop/state/sync.json";
|
|
3
|
+
/** A wiki page changed on both sides: the cloud version is written beside the local one under this suffix. */
|
|
4
|
+
export declare const CLOUD_COPY = ".cloud.md";
|
|
5
|
+
export type DocKind = 'board' | 'config' | 'lane' | 'wiki';
|
|
6
|
+
export type DocState = 'in-sync' | 'local-ahead' | 'cloud-ahead' | 'both-changed';
|
|
7
|
+
export interface SyncEntry {
|
|
8
|
+
kind: DocKind;
|
|
9
|
+
/** Empty until the first write reaches the cloud. */
|
|
10
|
+
pageId: string;
|
|
11
|
+
/** The page revision this checkout last saw. Every write to the page expects it. */
|
|
12
|
+
revision: number;
|
|
13
|
+
/** sha256 of the local file when it last matched the page at `revision`; null when it never has. */
|
|
14
|
+
hash: string | null;
|
|
15
|
+
/** A write that was kept locally because the cloud could not be reached. Pushed first by the next command. */
|
|
16
|
+
pending?: boolean;
|
|
17
|
+
/** Local and cloud differed when they first met and neither has been taken. */
|
|
18
|
+
diverged?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface SyncFile {
|
|
21
|
+
/** Keyed by the path under .codeloop: cards.json, config.yaml, lanes/<lane>.yaml, wiki/<folder>/<page>.md. */
|
|
22
|
+
documents: Record<string, SyncEntry>;
|
|
23
|
+
}
|
|
24
|
+
export interface CloudConfig {
|
|
25
|
+
url: string;
|
|
26
|
+
key: string;
|
|
27
|
+
workspaceName?: string;
|
|
28
|
+
boardTitle: string;
|
|
29
|
+
/** Root folder in the workspace. Lanes go in `<folder>/<folder>-lanes`, wiki pages in `<folder>/<folder>-wiki`. */
|
|
30
|
+
folder?: string;
|
|
31
|
+
}
|
|
32
|
+
export declare class CloudError extends Error {
|
|
33
|
+
}
|
|
34
|
+
/** The page moved on since this project last saw it: someone else wrote first. */
|
|
35
|
+
export declare class CloudConflictError extends CloudError {
|
|
36
|
+
}
|
|
37
|
+
export declare function loadCloud(projectDir: string): CloudConfig | null;
|
|
38
|
+
export declare function loadSync(projectDir: string): SyncFile;
|
|
39
|
+
type Call = {
|
|
40
|
+
name: string;
|
|
41
|
+
args: Record<string, unknown>;
|
|
42
|
+
};
|
|
43
|
+
/** Runs tool calls in order and returns each one's `data`. Throws CloudConflictError on VERSION_CONFLICT. */
|
|
44
|
+
export declare function callTools(conn: {
|
|
45
|
+
url: string;
|
|
46
|
+
key: string;
|
|
47
|
+
}, calls: Call[], timeoutMs?: number): any[];
|
|
48
|
+
export interface ConnectResult {
|
|
49
|
+
adopted: boolean;
|
|
50
|
+
revision: number;
|
|
51
|
+
wikiPages: number;
|
|
52
|
+
/** Documents that differ from a page already in the workspace, one line each. */
|
|
53
|
+
notes: string[];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* A board page that already exists under the same title is adopted as it is, never overwritten:
|
|
57
|
+
* another checkout may have written it. `cloud pull` takes it, `cloud push --force` replaces it.
|
|
58
|
+
* Config, lanes and wiki pages that are not in the workspace yet are uploaded.
|
|
59
|
+
*/
|
|
60
|
+
export declare function connect(projectDir: string, input: {
|
|
61
|
+
url: string;
|
|
62
|
+
key: string;
|
|
63
|
+
workspaceName?: string;
|
|
64
|
+
boardTitle?: string;
|
|
65
|
+
folder?: string;
|
|
66
|
+
}): ConnectResult;
|
|
67
|
+
/**
|
|
68
|
+
* Called by every card write before the local file changes. A stale revision throws
|
|
69
|
+
* CloudConflictError and the caller leaves the local file alone. If the cloud cannot be reached
|
|
70
|
+
* the write stays local and is marked pending in sync.json.
|
|
71
|
+
*/
|
|
72
|
+
export declare function writeCloudBoard(projectDir: string, content: string): void;
|
|
73
|
+
export declare function writeCloudWikiPage(projectDir: string, path: string): void;
|
|
74
|
+
/** Called by `lane promote` and `lane rollback` before the lane file is replaced: the only way a lane edit reaches the cloud. */
|
|
75
|
+
export declare function writeCloudLane(projectDir: string, laneId: string, content: string): void;
|
|
76
|
+
/**
|
|
77
|
+
* Run before a command: pending writes are pushed, files nobody edited here are updated from the
|
|
78
|
+
* cloud, and wiki pages and config edited here are pushed. Returns what it did, one line each.
|
|
79
|
+
* With the cloud unreachable the command still runs on the local files.
|
|
80
|
+
*/
|
|
81
|
+
export declare function syncBefore(projectDir: string): string[];
|
|
82
|
+
export declare function push(projectDir: string, opts?: {
|
|
83
|
+
force?: boolean;
|
|
84
|
+
}): {
|
|
85
|
+
revision: number;
|
|
86
|
+
wikiPages: number;
|
|
87
|
+
held: string[];
|
|
88
|
+
};
|
|
89
|
+
export declare function pull(projectDir: string, opts?: {
|
|
90
|
+
force?: boolean;
|
|
91
|
+
}): {
|
|
92
|
+
revision: number;
|
|
93
|
+
wikiPages: number;
|
|
94
|
+
notes: string[];
|
|
95
|
+
};
|
|
96
|
+
export declare function status(projectDir: string): {
|
|
97
|
+
url: string;
|
|
98
|
+
workspaceName: string | undefined;
|
|
99
|
+
boardTitle: string;
|
|
100
|
+
pageRevision: number;
|
|
101
|
+
lastSeenRevision: number;
|
|
102
|
+
localVersion: number;
|
|
103
|
+
cards: number;
|
|
104
|
+
inSync: boolean;
|
|
105
|
+
wikiPages: number;
|
|
106
|
+
documents: {
|
|
107
|
+
pending?: boolean | undefined;
|
|
108
|
+
path: string;
|
|
109
|
+
kind: DocKind;
|
|
110
|
+
state: DocState;
|
|
111
|
+
}[];
|
|
112
|
+
};
|
|
113
|
+
/** Pushes what is pending, brings every document back into the repo files, then forgets the connection. */
|
|
114
|
+
export declare function disconnect(projectDir: string): {
|
|
115
|
+
revision: number;
|
|
116
|
+
wikiPages: number;
|
|
117
|
+
};
|
|
118
|
+
export declare function search(projectDir: string, query: string, limit?: number): {
|
|
119
|
+
title: string;
|
|
120
|
+
id: string;
|
|
121
|
+
}[];
|
|
122
|
+
export {};
|