threadit-cli 0.2.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/assets/install.sh +16 -0
- package/assets/pack/capture-directive.md +1 -0
- package/assets/skills/threadit-orient/SKILL.md +15 -0
- package/assets/skills/threadit-reconcile/SKILL.md +15 -0
- package/dist/assets.d.ts +3 -0
- package/dist/assets.js +16 -0
- package/dist/bin.d.ts +11 -0
- package/dist/bin.js +472 -0
- package/dist/commands/accept.d.ts +12 -0
- package/dist/commands/accept.js +21 -0
- package/dist/commands/add.d.ts +11 -0
- package/dist/commands/add.js +19 -0
- package/dist/commands/capture.d.ts +24 -0
- package/dist/commands/capture.js +45 -0
- package/dist/commands/doc.d.ts +7 -0
- package/dist/commands/doc.js +56 -0
- package/dist/commands/drop.d.ts +4 -0
- package/dist/commands/drop.js +12 -0
- package/dist/commands/fold.d.ts +1 -0
- package/dist/commands/fold.js +9 -0
- package/dist/commands/hook.d.ts +24 -0
- package/dist/commands/hook.js +52 -0
- package/dist/commands/init.d.ts +18 -0
- package/dist/commands/init.js +55 -0
- package/dist/commands/ls.d.ts +8 -0
- package/dist/commands/ls.js +27 -0
- package/dist/commands/merge.d.ts +4 -0
- package/dist/commands/merge.js +18 -0
- package/dist/commands/move.d.ts +11 -0
- package/dist/commands/move.js +45 -0
- package/dist/commands/pack.d.ts +5 -0
- package/dist/commands/pack.js +12 -0
- package/dist/commands/park.d.ts +2 -0
- package/dist/commands/park.js +10 -0
- package/dist/commands/promote.d.ts +11 -0
- package/dist/commands/promote.js +33 -0
- package/dist/commands/reconcile.d.ts +12 -0
- package/dist/commands/reconcile.js +97 -0
- package/dist/commands/serve.d.ts +17 -0
- package/dist/commands/serve.js +39 -0
- package/dist/commands/ship.d.ts +9 -0
- package/dist/commands/ship.js +28 -0
- package/dist/commands/show.d.ts +2 -0
- package/dist/commands/show.js +16 -0
- package/dist/commands/snooze.d.ts +5 -0
- package/dist/commands/snooze.js +16 -0
- package/dist/commands/status.d.ts +14 -0
- package/dist/commands/status.js +47 -0
- package/dist/commands/supersede.d.ts +1 -0
- package/dist/commands/supersede.js +9 -0
- package/dist/commands/sync.d.ts +16 -0
- package/dist/commands/sync.js +66 -0
- package/dist/commands/update.d.ts +13 -0
- package/dist/commands/update.js +23 -0
- package/dist/commands/validate.d.ts +18 -0
- package/dist/commands/validate.js +52 -0
- package/dist/commands/wrapup.d.ts +3 -0
- package/dist/commands/wrapup.js +10 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +20 -0
- package/dist/draft.d.ts +29 -0
- package/dist/draft.js +59 -0
- package/dist/git/commitFacts.d.ts +6 -0
- package/dist/git/commitFacts.js +56 -0
- package/dist/git/log.d.ts +41 -0
- package/dist/git/log.js +143 -0
- package/dist/git/reconcileCommits.d.ts +3 -0
- package/dist/git/reconcileCommits.js +18 -0
- package/dist/git/run.d.ts +2 -0
- package/dist/git/run.js +8 -0
- package/dist/ids.d.ts +4 -0
- package/dist/ids.js +25 -0
- package/dist/inbox/recurrence.d.ts +7 -0
- package/dist/inbox/recurrence.js +51 -0
- package/dist/install/atomicWrite.d.ts +2 -0
- package/dist/install/atomicWrite.js +9 -0
- package/dist/install/gitHooks.d.ts +6 -0
- package/dist/install/gitHooks.js +53 -0
- package/dist/install/gitignore.d.ts +4 -0
- package/dist/install/gitignore.js +29 -0
- package/dist/install/settings.d.ts +7 -0
- package/dist/install/settings.js +40 -0
- package/dist/install/skills.d.ts +11 -0
- package/dist/install/skills.js +38 -0
- package/dist/install/tty.d.ts +6 -0
- package/dist/install/tty.js +39 -0
- package/dist/mutate.d.ts +6 -0
- package/dist/mutate.js +21 -0
- package/dist/paths.d.ts +13 -0
- package/dist/paths.js +46 -0
- package/dist/reconcileFormat.d.ts +3 -0
- package/dist/reconcileFormat.js +30 -0
- package/dist/session.d.ts +4 -0
- package/dist/session.js +11 -0
- package/dist/transport/sync.d.ts +38 -0
- package/dist/transport/sync.js +64 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.js +34 -0
- package/dist/yaml/emit.d.ts +3 -0
- package/dist/yaml/emit.js +50 -0
- package/dist/yaml/io.d.ts +11 -0
- package/dist/yaml/io.js +33 -0
- package/package.json +32 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Human-readable, class-grouped rendering of the unified proposal list. */
|
|
2
|
+
export function formatProposals(proposals) {
|
|
3
|
+
if (proposals.length === 0)
|
|
4
|
+
return 'Nothing to reconcile — inbox drained, statuses honest, no loops.\n';
|
|
5
|
+
const inbox = proposals.filter((p) => p.class === 'inbox');
|
|
6
|
+
const status = proposals.filter((p) => p.class === 'status');
|
|
7
|
+
const loops = proposals.filter((p) => p.class === 'loop');
|
|
8
|
+
let out = 'Proposed dispositions (run with --apply to execute):\n';
|
|
9
|
+
if (inbox.length) {
|
|
10
|
+
out += ' Inbox:\n';
|
|
11
|
+
for (const p of inbox)
|
|
12
|
+
out += ` ${p.itemId} (seen ${p.seenCount}): ${p.suggested} — ${p.text}\n`;
|
|
13
|
+
}
|
|
14
|
+
if (status.length) {
|
|
15
|
+
out += ' Status:\n';
|
|
16
|
+
for (const p of status) {
|
|
17
|
+
const label = `${p.nodeId} [${p.finding}${p.forced ? '' : ', advisory'}]`;
|
|
18
|
+
// advisory findings omit suggested; print evidence only
|
|
19
|
+
out += p.suggested !== undefined
|
|
20
|
+
? ` ${label}: ${p.suggested} — ${p.evidence}\n`
|
|
21
|
+
: ` ${label}: ${p.evidence}\n`;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (loops.length) {
|
|
25
|
+
out += ' Loops (un-captured):\n';
|
|
26
|
+
for (const p of loops)
|
|
27
|
+
out += ` ${p.sha.slice(0, 8)}: ${p.suggested} — ${p.subject}\n`;
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** The current session number (0 when never initialized / bumped). */
|
|
2
|
+
export declare function readSession(repoRoot: string): number;
|
|
3
|
+
/** Increment the session counter, persist it, and return the new value. */
|
|
4
|
+
export declare function bumpSession(repoRoot: string): number;
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { readConfig, updateConfig } from './config.js';
|
|
2
|
+
/** The current session number (0 when never initialized / bumped). */
|
|
3
|
+
export function readSession(repoRoot) {
|
|
4
|
+
return readConfig(repoRoot).session ?? 0;
|
|
5
|
+
}
|
|
6
|
+
/** Increment the session counter, persist it, and return the new value. */
|
|
7
|
+
export function bumpSession(repoRoot) {
|
|
8
|
+
const next = readSession(repoRoot) + 1;
|
|
9
|
+
updateConfig(repoRoot, { session: next });
|
|
10
|
+
return next;
|
|
11
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ChainEntry } from '../git/log.js';
|
|
2
|
+
export interface RevisionPayload {
|
|
3
|
+
projectId: string;
|
|
4
|
+
sha: string;
|
|
5
|
+
parentSha: string | null;
|
|
6
|
+
authorDate: string;
|
|
7
|
+
fileBlob: string;
|
|
8
|
+
trailers: string[];
|
|
9
|
+
touchedPaths: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface DraftPayload {
|
|
12
|
+
projectId: string;
|
|
13
|
+
sessionKey: string;
|
|
14
|
+
fileBlob: string;
|
|
15
|
+
}
|
|
16
|
+
export interface SyncDeps {
|
|
17
|
+
fetch: typeof globalThis.fetch;
|
|
18
|
+
}
|
|
19
|
+
/** Build ingest payloads (oldest-first) from a newest-first chain + per-sha lookups. */
|
|
20
|
+
export declare function buildRevisionPayloads(projectId: string, chainNewestFirst: ChainEntry[], blobAt: (sha: string) => string | null, trailersAt: (sha: string) => string[], pathsAt: (sha: string) => string[]): RevisionPayload[];
|
|
21
|
+
export interface IngestServerInfo {
|
|
22
|
+
serverProtocol: number;
|
|
23
|
+
serverVersion: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function postIngest(server: string, token: string, payload: RevisionPayload, deps: SyncDeps): Promise<IngestServerInfo>;
|
|
26
|
+
export declare function postDraft(server: string, token: string, payload: DraftPayload, deps: SyncDeps): Promise<void>;
|
|
27
|
+
export interface InboxPayload {
|
|
28
|
+
projectId: string;
|
|
29
|
+
fileBlob: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function postInbox(server: string, token: string, payload: InboxPayload, deps: SyncDeps): Promise<void>;
|
|
32
|
+
/** The full current first-parent sha set, sent on a detected history rewrite so the server purges
|
|
33
|
+
* orphaned shas (rebase/amend/force-push auto-recovery). */
|
|
34
|
+
export interface ManifestPayload {
|
|
35
|
+
projectId: string;
|
|
36
|
+
shas: string[];
|
|
37
|
+
}
|
|
38
|
+
export declare function postManifest(server: string, token: string, payload: ManifestPayload, deps: SyncDeps): Promise<void>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { INGEST_PROTOCOL } from '@threadit/core';
|
|
2
|
+
import { PACK_VERSION } from '../version.js';
|
|
3
|
+
/** Build ingest payloads (oldest-first) from a newest-first chain + per-sha lookups. */
|
|
4
|
+
export function buildRevisionPayloads(projectId, chainNewestFirst, blobAt, trailersAt, pathsAt) {
|
|
5
|
+
const payloads = [];
|
|
6
|
+
for (const entry of [...chainNewestFirst].reverse()) { // oldest-first
|
|
7
|
+
const fileBlob = blobAt(entry.sha);
|
|
8
|
+
if (fileBlob === null)
|
|
9
|
+
continue; // threadit.yml didn't exist at this revision — skip
|
|
10
|
+
payloads.push({
|
|
11
|
+
projectId,
|
|
12
|
+
sha: entry.sha,
|
|
13
|
+
parentSha: entry.parent,
|
|
14
|
+
authorDate: entry.authorDate,
|
|
15
|
+
fileBlob,
|
|
16
|
+
trailers: trailersAt(entry.sha),
|
|
17
|
+
touchedPaths: pathsAt(entry.sha),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return payloads;
|
|
21
|
+
}
|
|
22
|
+
export async function postIngest(server, token, payload, deps) {
|
|
23
|
+
const res = await deps.fetch(`${server}/projects/${payload.projectId}/ingest`, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`,
|
|
26
|
+
'X-Threadit-Protocol': String(INGEST_PROTOCOL), 'X-Threadit-Client': PACK_VERSION }, // X-Threadit-Client is one-way: server logs/ignores it; advisory is computed client-side
|
|
27
|
+
body: JSON.stringify(payload),
|
|
28
|
+
});
|
|
29
|
+
if (res.status === 409) {
|
|
30
|
+
const b = await res.json().catch(() => ({}));
|
|
31
|
+
throw new Error(`ingest rejected (protocol): ${b.message ?? 'version incompatible'}`);
|
|
32
|
+
}
|
|
33
|
+
if (!res.ok)
|
|
34
|
+
throw new Error(`ingest ${payload.sha} failed: HTTP ${res.status}`);
|
|
35
|
+
const b = await res.json().catch(() => ({}));
|
|
36
|
+
return { serverProtocol: b.serverProtocol ?? INGEST_PROTOCOL, serverVersion: b.serverVersion ?? '0.0.0' };
|
|
37
|
+
}
|
|
38
|
+
export async function postDraft(server, token, payload, deps) {
|
|
39
|
+
const res = await deps.fetch(`${server}/projects/${payload.projectId}/draft`, {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
|
42
|
+
body: JSON.stringify(payload),
|
|
43
|
+
});
|
|
44
|
+
if (!res.ok)
|
|
45
|
+
throw new Error(`draft sync failed: HTTP ${res.status}`);
|
|
46
|
+
}
|
|
47
|
+
export async function postInbox(server, token, payload, deps) {
|
|
48
|
+
const res = await deps.fetch(`${server}/projects/${payload.projectId}/inbox-sync`, {
|
|
49
|
+
method: 'POST',
|
|
50
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
|
51
|
+
body: JSON.stringify(payload),
|
|
52
|
+
});
|
|
53
|
+
if (!res.ok)
|
|
54
|
+
throw new Error(`inbox sync failed: HTTP ${res.status}`);
|
|
55
|
+
}
|
|
56
|
+
export async function postManifest(server, token, payload, deps) {
|
|
57
|
+
const res = await deps.fetch(`${server}/projects/${payload.projectId}/sync-manifest`, {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
|
60
|
+
body: JSON.stringify(payload),
|
|
61
|
+
});
|
|
62
|
+
if (!res.ok)
|
|
63
|
+
throw new Error(`sync-manifest failed: HTTP ${res.status}`);
|
|
64
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const PACK_VERSION: string;
|
|
2
|
+
/** A dev/prerelease/unbumped version that must never trigger a drift nudge. */
|
|
3
|
+
export declare function isDevVersion(v: string): boolean;
|
|
4
|
+
/** True iff `a` is strictly older than `b` (semver core compare). Equal/newer → false. */
|
|
5
|
+
export declare function isOlder(a: string, b: string): boolean;
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
function readPkgVersion() {
|
|
5
|
+
try {
|
|
6
|
+
// src/version.ts (vitest) or dist/version.js (shipped) → ../package.json at the package root
|
|
7
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const pkg = JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf8'));
|
|
9
|
+
return pkg.version ?? '0.0.0';
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return '0.0.0';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export const PACK_VERSION = readPkgVersion();
|
|
16
|
+
/** A dev/prerelease/unbumped version that must never trigger a drift nudge. */
|
|
17
|
+
export function isDevVersion(v) {
|
|
18
|
+
return v === '0.0.0' || v.includes('-');
|
|
19
|
+
}
|
|
20
|
+
function triple(v) {
|
|
21
|
+
const core = v.split('-')[0];
|
|
22
|
+
const [a, b, c] = core.split('.').map((n) => Number.parseInt(n, 10) || 0);
|
|
23
|
+
return [a ?? 0, b ?? 0, c ?? 0];
|
|
24
|
+
}
|
|
25
|
+
/** True iff `a` is strictly older than `b` (semver core compare). Equal/newer → false. */
|
|
26
|
+
export function isOlder(a, b) {
|
|
27
|
+
const [a0, a1, a2] = triple(a);
|
|
28
|
+
const [b0, b1, b2] = triple(b);
|
|
29
|
+
if (a0 !== b0)
|
|
30
|
+
return a0 < b0;
|
|
31
|
+
if (a1 !== b1)
|
|
32
|
+
return a1 < b1;
|
|
33
|
+
return a2 < b2;
|
|
34
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { stringify } from 'yaml';
|
|
2
|
+
const NODE_KEY_ORDER = [
|
|
3
|
+
'id', 'parent', 'order', 'title', 'status', 'flow', 'milestone', 'main',
|
|
4
|
+
'summary', 'summary_at', 'detail', 'work', 'paths', 'docs',
|
|
5
|
+
'blocked_by', 'born_from', 'superseded_by', 'folded_into', 'renamed_from', 'related',
|
|
6
|
+
'until', 'decisions', 'at',
|
|
7
|
+
];
|
|
8
|
+
const INBOX_ITEM_KEY_ORDER = [
|
|
9
|
+
'id', 'text', 'created', 'source', 'session_id', 'status',
|
|
10
|
+
'seen_count', 'seen_at', 'snooze_until', 'promoted_to', 'related', 'tags', 'note', 'risk_node',
|
|
11
|
+
];
|
|
12
|
+
/**
|
|
13
|
+
* Reorder an object's present keys into a fixed canonical order.
|
|
14
|
+
*
|
|
15
|
+
* Contract: a key whose value is `undefined` is OMITTED; a key whose value is `null`
|
|
16
|
+
* is EMITTED as `null`. Callers clearing a nullable field must `delete` the key (omit) —
|
|
17
|
+
* setting it to `undefined` drops it; the field's absence vs explicit null is meaningful.
|
|
18
|
+
*/
|
|
19
|
+
function orderKeys(obj, keyOrder) {
|
|
20
|
+
const out = {};
|
|
21
|
+
for (const k of keyOrder)
|
|
22
|
+
if (obj[k] !== undefined)
|
|
23
|
+
out[k] = obj[k];
|
|
24
|
+
for (const k of Object.keys(obj))
|
|
25
|
+
if (!keyOrder.includes(k) && obj[k] !== undefined)
|
|
26
|
+
out[k] = obj[k];
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
export function emitThreaditFile(file) {
|
|
30
|
+
const doc = { threadit: file.threadit, project: file.project };
|
|
31
|
+
// omit optional top-level keys rather than emit them as null
|
|
32
|
+
if (file.sessions !== undefined)
|
|
33
|
+
doc.sessions = file.sessions;
|
|
34
|
+
if (file.wrapup !== undefined)
|
|
35
|
+
doc.wrapup = file.wrapup;
|
|
36
|
+
if (file.reconcile !== undefined)
|
|
37
|
+
doc.reconcile = file.reconcile;
|
|
38
|
+
if (file.epochs !== undefined)
|
|
39
|
+
doc.epochs = file.epochs;
|
|
40
|
+
doc.nodes = file.nodes.map((n) => orderKeys(n, NODE_KEY_ORDER));
|
|
41
|
+
return stringify(doc, { lineWidth: 0 }); // lineWidth 0 = no line-wrap: stable canonical emit across editors
|
|
42
|
+
}
|
|
43
|
+
export function emitInboxFile(inbox) {
|
|
44
|
+
const doc = {
|
|
45
|
+
threadit_inbox: inbox.threadit_inbox,
|
|
46
|
+
project: inbox.project,
|
|
47
|
+
items: inbox.items.map((i) => orderKeys(i, INBOX_ITEM_KEY_ORDER)),
|
|
48
|
+
};
|
|
49
|
+
return stringify(doc, { lineWidth: 0 }); // lineWidth 0 = no line-wrap: stable canonical emit across editors
|
|
50
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ParsedThreaditFile, ParsedInboxFile, StructuralError } from '@threadit/core';
|
|
2
|
+
export declare class ValidationFailure extends Error {
|
|
3
|
+
readonly errors: StructuralError[];
|
|
4
|
+
constructor(errors: StructuralError[]);
|
|
5
|
+
}
|
|
6
|
+
export declare function loadThreadit(path: string): ParsedThreaditFile;
|
|
7
|
+
/** Persist a threadit.yml ONLY if it passes the structural gate (valid by construction). */
|
|
8
|
+
export declare function saveThreadit(path: string, file: ParsedThreaditFile): void;
|
|
9
|
+
export declare function loadInbox(path: string): ParsedInboxFile;
|
|
10
|
+
/** Inbox has no structural gate; re-parse the emitted text to guarantee it is well-formed. */
|
|
11
|
+
export declare function saveInbox(path: string, inbox: ParsedInboxFile): void;
|
package/dist/yaml/io.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { engine, parseThreaditFile, parseInboxFile } from '@threadit/core';
|
|
3
|
+
import { emitThreaditFile, emitInboxFile } from './emit.js';
|
|
4
|
+
export class ValidationFailure extends Error {
|
|
5
|
+
errors;
|
|
6
|
+
constructor(errors) {
|
|
7
|
+
super('Refusing to write: threadit.yml would be structurally invalid:\n' +
|
|
8
|
+
errors.map((e) => ` ${e.kind} at ${e.at}: ${e.message}`).join('\n'));
|
|
9
|
+
this.errors = errors;
|
|
10
|
+
this.name = 'ValidationFailure';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function loadThreadit(path) {
|
|
14
|
+
return parseThreaditFile(readFileSync(path, 'utf8'));
|
|
15
|
+
}
|
|
16
|
+
/** Persist a threadit.yml ONLY if it passes the structural gate (valid by construction). */
|
|
17
|
+
export function saveThreadit(path, file) {
|
|
18
|
+
const result = engine({ file });
|
|
19
|
+
if (result.structuralErrors.length > 0)
|
|
20
|
+
throw new ValidationFailure(result.structuralErrors);
|
|
21
|
+
const text = emitThreaditFile(file);
|
|
22
|
+
parseThreaditFile(text); // guard: emitted text must round-trip through the parser
|
|
23
|
+
writeFileSync(path, text, 'utf8');
|
|
24
|
+
}
|
|
25
|
+
export function loadInbox(path) {
|
|
26
|
+
return parseInboxFile(readFileSync(path, 'utf8'));
|
|
27
|
+
}
|
|
28
|
+
/** Inbox has no structural gate; re-parse the emitted text to guarantee it is well-formed. */
|
|
29
|
+
export function saveInbox(path, inbox) {
|
|
30
|
+
const text = emitInboxFile(inbox);
|
|
31
|
+
parseInboxFile(text); // throws ThreaditParseError if we produced something invalid
|
|
32
|
+
writeFileSync(path, text, 'utf8');
|
|
33
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "threadit-cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"threadit": "./dist/bin.js"
|
|
7
|
+
},
|
|
8
|
+
"main": "./dist/bin.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/bin.d.ts",
|
|
12
|
+
"default": "./dist/bin.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"assets"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"build": "tsc -b"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@threadit/core": "^0.2.0",
|
|
25
|
+
"commander": "^12.1.0",
|
|
26
|
+
"picomatch": "^4.0.2",
|
|
27
|
+
"yaml": "^2.5.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/picomatch": "^3.0.1"
|
|
31
|
+
}
|
|
32
|
+
}
|