forgepm 0.7.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/dist/bin/forgepm.d.ts +2 -0
- package/dist/bin/forgepm.js +4 -0
- package/dist/bin/forgepm.js.map +1 -0
- package/dist/src/backends/base.d.ts +23 -0
- package/dist/src/backends/base.js +2 -0
- package/dist/src/backends/base.js.map +1 -0
- package/dist/src/backends/http-utils.d.ts +6 -0
- package/dist/src/backends/http-utils.js +36 -0
- package/dist/src/backends/http-utils.js.map +1 -0
- package/dist/src/backends/jira.d.ts +16 -0
- package/dist/src/backends/jira.js +117 -0
- package/dist/src/backends/jira.js.map +1 -0
- package/dist/src/backends/linear.d.ts +11 -0
- package/dist/src/backends/linear.js +85 -0
- package/dist/src/backends/linear.js.map +1 -0
- package/dist/src/cli/archaeology.d.ts +38 -0
- package/dist/src/cli/archaeology.js +182 -0
- package/dist/src/cli/archaeology.js.map +1 -0
- package/dist/src/cli/index.d.ts +1 -0
- package/dist/src/cli/index.js +351 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/core/event-bus.d.ts +17 -0
- package/dist/src/core/event-bus.js +33 -0
- package/dist/src/core/event-bus.js.map +1 -0
- package/dist/src/core/event-plugins.d.ts +6 -0
- package/dist/src/core/event-plugins.js +2 -0
- package/dist/src/core/event-plugins.js.map +1 -0
- package/dist/src/core/forge-ops.d.ts +42 -0
- package/dist/src/core/forge-ops.js +101 -0
- package/dist/src/core/forge-ops.js.map +1 -0
- package/dist/src/core/forge.d.ts +44 -0
- package/dist/src/core/forge.js +86 -0
- package/dist/src/core/forge.js.map +1 -0
- package/dist/src/core/schema.d.ts +8 -0
- package/dist/src/core/schema.js +139 -0
- package/dist/src/core/schema.js.map +1 -0
- package/dist/src/core/store.d.ts +24 -0
- package/dist/src/core/store.js +155 -0
- package/dist/src/core/store.js.map +1 -0
- package/dist/src/core/types.d.ts +91 -0
- package/dist/src/core/types.js +19 -0
- package/dist/src/core/types.js.map +1 -0
- package/dist/src/core/version.d.ts +1 -0
- package/dist/src/core/version.js +20 -0
- package/dist/src/core/version.js.map +1 -0
- package/dist/src/github/pull.d.ts +13 -0
- package/dist/src/github/pull.js +51 -0
- package/dist/src/github/pull.js.map +1 -0
- package/dist/src/github/push.d.ts +20 -0
- package/dist/src/github/push.js +118 -0
- package/dist/src/github/push.js.map +1 -0
- package/dist/src/github/setup.d.ts +10 -0
- package/dist/src/github/setup.js +112 -0
- package/dist/src/github/setup.js.map +1 -0
- package/dist/src/github/slack.d.ts +12 -0
- package/dist/src/github/slack.js +33 -0
- package/dist/src/github/slack.js.map +1 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +9 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/mcp/server.d.ts +5 -0
- package/dist/src/mcp/server.js +69 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/mcp/tool-handlers.d.ts +19 -0
- package/dist/src/mcp/tool-handlers.js +123 -0
- package/dist/src/mcp/tool-handlers.js.map +1 -0
- package/dist/src/pm/calibration.d.ts +13 -0
- package/dist/src/pm/calibration.js +56 -0
- package/dist/src/pm/calibration.js.map +1 -0
- package/dist/src/pm/callsheet.d.ts +66 -0
- package/dist/src/pm/callsheet.js +91 -0
- package/dist/src/pm/callsheet.js.map +1 -0
- package/dist/src/pm/drift-detector.d.ts +38 -0
- package/dist/src/pm/drift-detector.js +111 -0
- package/dist/src/pm/drift-detector.js.map +1 -0
- package/dist/src/pm/evm.d.ts +31 -0
- package/dist/src/pm/evm.js +58 -0
- package/dist/src/pm/evm.js.map +1 -0
- package/dist/src/pm/pdca.d.ts +27 -0
- package/dist/src/pm/pdca.js +83 -0
- package/dist/src/pm/pdca.js.map +1 -0
- package/dist/src/pm/recall.d.ts +23 -0
- package/dist/src/pm/recall.js +126 -0
- package/dist/src/pm/recall.js.map +1 -0
- package/dist/src/pm/retrospective.d.ts +35 -0
- package/dist/src/pm/retrospective.js +104 -0
- package/dist/src/pm/retrospective.js.map +1 -0
- package/dist/src/pm/sessions.d.ts +36 -0
- package/dist/src/pm/sessions.js +91 -0
- package/dist/src/pm/sessions.js.map +1 -0
- package/dist/src/pm/tolerances.d.ts +21 -0
- package/dist/src/pm/tolerances.js +54 -0
- package/dist/src/pm/tolerances.js.map +1 -0
- package/dist/src/utils/validation.d.ts +2 -0
- package/dist/src/utils/validation.js +8 -0
- package/dist/src/utils/validation.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forgepm.js","sourceRoot":"","sources":["../../bin/forgepm.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface TaskData {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
status: string;
|
|
5
|
+
complexity: number;
|
|
6
|
+
description?: string;
|
|
7
|
+
why?: string;
|
|
8
|
+
externalId?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ExternalUpdate {
|
|
11
|
+
externalId: string;
|
|
12
|
+
localTaskId?: string;
|
|
13
|
+
state: 'open' | 'closed';
|
|
14
|
+
labels?: string[];
|
|
15
|
+
title?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface BackendAdapter {
|
|
18
|
+
name: string;
|
|
19
|
+
setup(config: Record<string, string>): Promise<void>;
|
|
20
|
+
pushTask(task: TaskData): Promise<string>;
|
|
21
|
+
closeTask(externalId: string): Promise<void>;
|
|
22
|
+
pullUpdates(since?: string): Promise<ExternalUpdate[]>;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../../src/backends/base.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare class HttpError extends Error {
|
|
2
|
+
readonly status: number;
|
|
3
|
+
readonly body: string;
|
|
4
|
+
constructor(status: number, body: string);
|
|
5
|
+
}
|
|
6
|
+
export declare function fetchWithRetry(url: string, init: RequestInit, maxRetries?: number, baseDelayMs?: number): Promise<Response>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
function redactSecrets(text) {
|
|
2
|
+
// Redact long alphanumeric strings that look like tokens/keys
|
|
3
|
+
return text.replace(/[A-Za-z0-9_-]{20,}/g, '[REDACTED]');
|
|
4
|
+
}
|
|
5
|
+
export class HttpError extends Error {
|
|
6
|
+
status;
|
|
7
|
+
body;
|
|
8
|
+
constructor(status, body) {
|
|
9
|
+
super(`HTTP ${status}: ${redactSecrets(body.slice(0, 200))}`);
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.body = body;
|
|
12
|
+
this.name = 'HttpError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function fetchWithRetry(url, init, maxRetries = 3, baseDelayMs = 500) {
|
|
16
|
+
let lastError = null;
|
|
17
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
18
|
+
const response = await fetch(url, init);
|
|
19
|
+
if (response.ok)
|
|
20
|
+
return response;
|
|
21
|
+
// Retry on rate limit (429) or server errors (5xx)
|
|
22
|
+
if (response.status === 429 || response.status >= 500) {
|
|
23
|
+
lastError = new HttpError(response.status, await response.text());
|
|
24
|
+
if (attempt < maxRetries) {
|
|
25
|
+
const delay = baseDelayMs * Math.pow(2, attempt);
|
|
26
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Non-retryable error
|
|
31
|
+
const body = await response.text();
|
|
32
|
+
throw new HttpError(response.status, body);
|
|
33
|
+
}
|
|
34
|
+
throw lastError ?? new Error('fetchWithRetry exhausted retries');
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=http-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-utils.js","sourceRoot":"","sources":["../../../src/backends/http-utils.ts"],"names":[],"mappings":"AAAA,SAAS,aAAa,CAAC,IAAY;IACjC,8DAA8D;IAC9D,OAAO,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,YAAY,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,OAAO,SAAU,SAAQ,KAAK;IAEhB;IACA;IAFlB,YACkB,MAAc,EACd,IAAY;QAE5B,KAAK,CAAC,QAAQ,MAAM,KAAK,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;QAH9C,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAQ;QAG5B,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAW,EACX,IAAiB,EACjB,UAAU,GAAG,CAAC,EACd,WAAW,GAAG,GAAG;IAEjB,IAAI,SAAS,GAAiB,IAAI,CAAC;IAEnC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAExC,IAAI,QAAQ,CAAC,EAAE;YAAE,OAAO,QAAQ,CAAC;QAEjC,mDAAmD;QACnD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;YACtD,SAAS,GAAG,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;YAClE,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;gBACzB,MAAM,KAAK,GAAG,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;gBACjD,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;gBACzD,SAAS;YACX,CAAC;QACH,CAAC;QAED,sBAAsB;QACtB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,SAAS,IAAI,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { BackendAdapter, TaskData, ExternalUpdate } from './base.js';
|
|
2
|
+
export declare class JiraBackend implements BackendAdapter {
|
|
3
|
+
name: string;
|
|
4
|
+
private baseUrl;
|
|
5
|
+
private projectKey;
|
|
6
|
+
private authHeader;
|
|
7
|
+
private doneTransitionId;
|
|
8
|
+
setup(config: Record<string, string>): Promise<void>;
|
|
9
|
+
pushTask(task: TaskData): Promise<string>;
|
|
10
|
+
closeTask(externalId: string): Promise<void>;
|
|
11
|
+
pullUpdates(since?: string): Promise<ExternalUpdate[]>;
|
|
12
|
+
private findDoneTransition;
|
|
13
|
+
private rest;
|
|
14
|
+
private toAdf;
|
|
15
|
+
private escapeJql;
|
|
16
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { fetchWithRetry } from './http-utils.js';
|
|
2
|
+
export class JiraBackend {
|
|
3
|
+
name = 'jira';
|
|
4
|
+
baseUrl = '';
|
|
5
|
+
projectKey = '';
|
|
6
|
+
authHeader = '';
|
|
7
|
+
doneTransitionId = null;
|
|
8
|
+
async setup(config) {
|
|
9
|
+
this.baseUrl = (config['JIRA_BASE_URL'] ?? process.env['JIRA_BASE_URL'] ?? '').replace(/\/$/, '');
|
|
10
|
+
const email = config['JIRA_EMAIL'] ?? process.env['JIRA_EMAIL'] ?? '';
|
|
11
|
+
const apiToken = config['JIRA_API_TOKEN'] ?? process.env['JIRA_API_TOKEN'] ?? '';
|
|
12
|
+
this.projectKey = config['JIRA_PROJECT_KEY'] ?? process.env['JIRA_PROJECT_KEY'] ?? '';
|
|
13
|
+
this.doneTransitionId = config['JIRA_DONE_TRANSITION_ID'] ?? process.env['JIRA_DONE_TRANSITION_ID'] ?? null;
|
|
14
|
+
if (!this.baseUrl)
|
|
15
|
+
throw new Error('JIRA_BASE_URL is required (e.g. https://yourorg.atlassian.net)');
|
|
16
|
+
// SSRF prevention: validate URL schema and block internal IPs
|
|
17
|
+
const parsed = new URL(this.baseUrl);
|
|
18
|
+
if (!['https:', 'http:'].includes(parsed.protocol))
|
|
19
|
+
throw new Error('JIRA_BASE_URL must use http or https');
|
|
20
|
+
if (/^(localhost|127\.|10\.|192\.168\.|169\.254\.|172\.(1[6-9]|2\d|3[01])\.)/.test(parsed.hostname)) {
|
|
21
|
+
throw new Error('JIRA_BASE_URL points to internal address — blocked for security');
|
|
22
|
+
}
|
|
23
|
+
if (!email)
|
|
24
|
+
throw new Error('JIRA_EMAIL is required');
|
|
25
|
+
if (!apiToken)
|
|
26
|
+
throw new Error('JIRA_API_TOKEN is required');
|
|
27
|
+
if (!this.projectKey)
|
|
28
|
+
throw new Error('JIRA_PROJECT_KEY is required (e.g. PROJ)');
|
|
29
|
+
if (!/^[A-Z][A-Z0-9]{0,9}$/.test(this.projectKey))
|
|
30
|
+
throw new Error('JIRA_PROJECT_KEY must be uppercase alphanumeric (e.g. PROJ)');
|
|
31
|
+
this.authHeader = `Basic ${btoa(`${email}:${apiToken}`)}`;
|
|
32
|
+
}
|
|
33
|
+
async pushTask(task) {
|
|
34
|
+
if (task.externalId) {
|
|
35
|
+
await this.rest(`/rest/api/3/issue/${task.externalId}`, {
|
|
36
|
+
method: 'PUT',
|
|
37
|
+
body: JSON.stringify({
|
|
38
|
+
fields: {
|
|
39
|
+
summary: task.title,
|
|
40
|
+
description: this.toAdf(task.description ?? task.why ?? ''),
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
44
|
+
return task.externalId;
|
|
45
|
+
}
|
|
46
|
+
const result = await this.rest('/rest/api/3/issue', {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
body: JSON.stringify({
|
|
49
|
+
fields: {
|
|
50
|
+
project: { key: this.projectKey },
|
|
51
|
+
summary: task.title,
|
|
52
|
+
description: this.toAdf(task.why ?? task.description ?? `ForgePM task ${task.id}, complexity ${task.complexity}`),
|
|
53
|
+
issuetype: { name: 'Task' },
|
|
54
|
+
labels: ['forge:tracked', `complexity:${task.complexity}`],
|
|
55
|
+
},
|
|
56
|
+
}),
|
|
57
|
+
});
|
|
58
|
+
return result.key;
|
|
59
|
+
}
|
|
60
|
+
async closeTask(externalId) {
|
|
61
|
+
const transitionId = this.doneTransitionId ?? await this.findDoneTransition(externalId);
|
|
62
|
+
if (!transitionId)
|
|
63
|
+
return;
|
|
64
|
+
await this.rest(`/rest/api/3/issue/${externalId}/transitions`, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
body: JSON.stringify({ transition: { id: transitionId } }),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async pullUpdates(since) {
|
|
70
|
+
const jqlParts = [`project = "${this.projectKey}"`, 'labels = "forge:tracked"'];
|
|
71
|
+
if (since)
|
|
72
|
+
jqlParts.push(`updated >= "${this.escapeJql(since)}"`);
|
|
73
|
+
const jql = jqlParts.join(' AND ') + ' ORDER BY updated DESC';
|
|
74
|
+
const result = await this.rest(`/rest/api/3/search?jql=${encodeURIComponent(jql)}&maxResults=200&fields=summary,status,labels`, { method: 'GET' });
|
|
75
|
+
return result.issues.map(issue => ({
|
|
76
|
+
externalId: issue.key,
|
|
77
|
+
state: issue.fields.status.statusCategory.key === 'done' ? 'closed' : 'open',
|
|
78
|
+
labels: issue.fields.labels,
|
|
79
|
+
title: issue.fields.summary,
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
async findDoneTransition(issueKey) {
|
|
83
|
+
const result = await this.rest(`/rest/api/3/issue/${issueKey}/transitions`, { method: 'GET' });
|
|
84
|
+
const doneNames = ['done', 'closed', 'resolved', 'complete'];
|
|
85
|
+
// Prefer by statusCategory, then by name fuzzy match
|
|
86
|
+
const byCategory = result.transitions.find(t => t.to.statusCategory.key === 'done');
|
|
87
|
+
if (byCategory)
|
|
88
|
+
return byCategory.id;
|
|
89
|
+
const byName = result.transitions.find(t => doneNames.some(n => t.name.toLowerCase().includes(n)));
|
|
90
|
+
return byName?.id ?? null;
|
|
91
|
+
}
|
|
92
|
+
async rest(path, init) {
|
|
93
|
+
const response = await fetchWithRetry(`${this.baseUrl}${path}`, {
|
|
94
|
+
...init,
|
|
95
|
+
headers: {
|
|
96
|
+
'Content-Type': 'application/json',
|
|
97
|
+
'Accept': 'application/json',
|
|
98
|
+
'Authorization': this.authHeader,
|
|
99
|
+
...(init.headers ?? {}),
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
if (response.status === 204)
|
|
103
|
+
return {};
|
|
104
|
+
return response.json();
|
|
105
|
+
}
|
|
106
|
+
toAdf(text) {
|
|
107
|
+
return {
|
|
108
|
+
type: 'doc', version: 1,
|
|
109
|
+
content: [{ type: 'paragraph', content: [{ type: 'text', text: text || '(no description)' }] }],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
escapeJql(value) {
|
|
113
|
+
// Strip all non-date/time characters to prevent JQL injection
|
|
114
|
+
return value.replace(/[^0-9TZ.:/ -]/g, '');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=jira.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jira.js","sourceRoot":"","sources":["../../../src/backends/jira.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BackendAdapter, TaskData, ExternalUpdate } from './base.js';
|
|
2
|
+
export declare class LinearBackend implements BackendAdapter {
|
|
3
|
+
name: string;
|
|
4
|
+
private apiKey;
|
|
5
|
+
private teamId;
|
|
6
|
+
setup(config: Record<string, string>): Promise<void>;
|
|
7
|
+
pushTask(task: TaskData): Promise<string>;
|
|
8
|
+
closeTask(externalId: string): Promise<void>;
|
|
9
|
+
pullUpdates(since?: string): Promise<ExternalUpdate[]>;
|
|
10
|
+
private graphql;
|
|
11
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { fetchWithRetry } from './http-utils.js';
|
|
2
|
+
const LINEAR_API = 'https://api.linear.app/graphql';
|
|
3
|
+
export class LinearBackend {
|
|
4
|
+
name = 'linear';
|
|
5
|
+
apiKey = '';
|
|
6
|
+
teamId = '';
|
|
7
|
+
async setup(config) {
|
|
8
|
+
this.apiKey = config['LINEAR_API_KEY'] ?? process.env['LINEAR_API_KEY'] ?? '';
|
|
9
|
+
if (!this.apiKey)
|
|
10
|
+
throw new Error('LINEAR_API_KEY is required. Set it as an environment variable.');
|
|
11
|
+
this.teamId = config['LINEAR_TEAM_ID'] ?? process.env['LINEAR_TEAM_ID'] ?? '';
|
|
12
|
+
if (!this.teamId) {
|
|
13
|
+
// Auto-discover first team
|
|
14
|
+
const teams = await this.graphql('{ teams { nodes { id name } } }');
|
|
15
|
+
const firstTeam = teams.data.teams.nodes[0];
|
|
16
|
+
if (!firstTeam)
|
|
17
|
+
throw new Error('No Linear teams found. Create a team first.');
|
|
18
|
+
this.teamId = firstTeam.id;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async pushTask(task) {
|
|
22
|
+
if (task.externalId) {
|
|
23
|
+
// Update existing issue
|
|
24
|
+
await this.graphql(`mutation($id: String!, $title: String!, $description: String) {
|
|
25
|
+
issueUpdate(id: $id, input: { title: $title, description: $description }) { success }
|
|
26
|
+
}`, { id: task.externalId, title: task.title, description: task.description ?? '' });
|
|
27
|
+
return task.externalId;
|
|
28
|
+
}
|
|
29
|
+
// Create new issue
|
|
30
|
+
const complexityLabel = `complexity:${task.complexity}`;
|
|
31
|
+
const result = await this.graphql(`mutation($teamId: String!, $title: String!, $description: String) {
|
|
32
|
+
issueCreate(input: { teamId: $teamId, title: $title, description: $description }) {
|
|
33
|
+
issue { id identifier }
|
|
34
|
+
}
|
|
35
|
+
}`, { teamId: this.teamId, title: `[${complexityLabel}] ${task.title}`, description: task.why ?? task.description ?? '' });
|
|
36
|
+
return result.data.issueCreate.issue.id;
|
|
37
|
+
}
|
|
38
|
+
async closeTask(externalId) {
|
|
39
|
+
// Find "Done" state
|
|
40
|
+
const states = await this.graphql(`query($teamId: String!) {
|
|
41
|
+
workflowStates(filter: { team: { id: { eq: $teamId } }, type: { eq: "completed" } }) {
|
|
42
|
+
nodes { id name type }
|
|
43
|
+
}
|
|
44
|
+
}`, { teamId: this.teamId });
|
|
45
|
+
const doneState = states.data.workflowStates.nodes[0];
|
|
46
|
+
if (!doneState)
|
|
47
|
+
return;
|
|
48
|
+
await this.graphql(`mutation($id: String!, $stateId: String!) {
|
|
49
|
+
issueUpdate(id: $id, input: { stateId: $stateId }) { success }
|
|
50
|
+
}`, { id: externalId, stateId: doneState.id });
|
|
51
|
+
}
|
|
52
|
+
async pullUpdates(since) {
|
|
53
|
+
const query = since
|
|
54
|
+
? `query($teamId: String!, $since: DateTime!) {
|
|
55
|
+
issues(filter: { team: { id: { eq: $teamId } }, updatedAt: { gt: $since } }, first: 100) {
|
|
56
|
+
nodes { id title state { type } labels { nodes { name } } }
|
|
57
|
+
}
|
|
58
|
+
}`
|
|
59
|
+
: `query($teamId: String!) {
|
|
60
|
+
issues(filter: { team: { id: { eq: $teamId } } }, first: 100) {
|
|
61
|
+
nodes { id title state { type } labels { nodes { name } } }
|
|
62
|
+
}
|
|
63
|
+
}`;
|
|
64
|
+
const variables = since ? { teamId: this.teamId, since } : { teamId: this.teamId };
|
|
65
|
+
const result = await this.graphql(query, variables);
|
|
66
|
+
return result.data.issues.nodes.map(issue => ({
|
|
67
|
+
externalId: issue.id,
|
|
68
|
+
state: issue.state.type === 'completed' || issue.state.type === 'canceled' ? 'closed' : 'open',
|
|
69
|
+
labels: issue.labels.nodes.map(l => l.name),
|
|
70
|
+
title: issue.title,
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
async graphql(query, variables) {
|
|
74
|
+
const response = await fetchWithRetry(LINEAR_API, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: {
|
|
77
|
+
'Content-Type': 'application/json',
|
|
78
|
+
'Authorization': this.apiKey,
|
|
79
|
+
},
|
|
80
|
+
body: JSON.stringify({ query, variables }),
|
|
81
|
+
});
|
|
82
|
+
return response.json();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=linear.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear.js","sourceRoot":"","sources":["../../../src/backends/linear.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { OverlayStore } from '../core/store.js';
|
|
2
|
+
export interface ArchaeologyOptions {
|
|
3
|
+
since: string;
|
|
4
|
+
dryRun: boolean;
|
|
5
|
+
github: boolean;
|
|
6
|
+
force: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface ArchaeologyTask {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
complexity: number;
|
|
12
|
+
type: string;
|
|
13
|
+
author: string;
|
|
14
|
+
date: string;
|
|
15
|
+
linesChanged: number;
|
|
16
|
+
}
|
|
17
|
+
export interface ArchaeologyResult {
|
|
18
|
+
commitsAnalyzed: number;
|
|
19
|
+
tasksCreated: number;
|
|
20
|
+
githubIssuesImported: number;
|
|
21
|
+
calibrationUpdated: boolean;
|
|
22
|
+
tasks: ArchaeologyTask[];
|
|
23
|
+
}
|
|
24
|
+
export declare class ArchaeologyEngine {
|
|
25
|
+
private store;
|
|
26
|
+
constructor(store: OverlayStore);
|
|
27
|
+
run(cwd: string, opts: ArchaeologyOptions, onProgress?: (i: number, total: number, title: string) => void): Promise<ArchaeologyResult>;
|
|
28
|
+
private verifyGit;
|
|
29
|
+
private parseGitLog;
|
|
30
|
+
private classifyCommit;
|
|
31
|
+
private estimateComplexity;
|
|
32
|
+
private sanitize;
|
|
33
|
+
private persistTasks;
|
|
34
|
+
private getCalibrationEstimate;
|
|
35
|
+
private recalibrateFromImport;
|
|
36
|
+
importGitHubIssues(cwd: string, existingTasks: ArchaeologyTask[]): Promise<ArchaeologyTask[]>;
|
|
37
|
+
private inferTypeFromLabels;
|
|
38
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { getModelTier } from '../core/types.js';
|
|
4
|
+
const execFileAsync = promisify(execFile);
|
|
5
|
+
const COMMIT_TYPES = {
|
|
6
|
+
feat: 'feature', fix: 'bugfix', refactor: 'refactor',
|
|
7
|
+
docs: 'docs', test: 'test', chore: 'chore', perf: 'perf',
|
|
8
|
+
};
|
|
9
|
+
const CONVENTIONAL_RE = /^(feat|fix|refactor|docs|test|chore|perf)(\(.+?\))?!?:\s*(.+)/;
|
|
10
|
+
const SHORTSTAT_RE = /(\d+) files? changed(?:,\s+(\d+) insertions?\(\+\))?(?:,\s+(\d+) deletions?\(-\))?/;
|
|
11
|
+
export class ArchaeologyEngine {
|
|
12
|
+
store;
|
|
13
|
+
constructor(store) {
|
|
14
|
+
this.store = store;
|
|
15
|
+
}
|
|
16
|
+
async run(cwd, opts, onProgress) {
|
|
17
|
+
await this.verifyGit(cwd);
|
|
18
|
+
const commits = await this.parseGitLog(cwd, opts.since);
|
|
19
|
+
if (commits.length === 0) {
|
|
20
|
+
return { commitsAnalyzed: 0, tasksCreated: 0, githubIssuesImported: 0, calibrationUpdated: false, tasks: [] };
|
|
21
|
+
}
|
|
22
|
+
const tasks = [];
|
|
23
|
+
for (let i = 0; i < commits.length; i++) {
|
|
24
|
+
const commit = commits[i];
|
|
25
|
+
const { type, title } = this.classifyCommit(commit.subject);
|
|
26
|
+
const linesChanged = commit.insertions + commit.deletions;
|
|
27
|
+
const complexity = this.estimateComplexity(linesChanged);
|
|
28
|
+
const taskId = opts.dryRun ? `TASK-${String(i + 1).padStart(3, '0')}` : this.store.nextId('TASK');
|
|
29
|
+
tasks.push({ id: taskId, title, complexity, type, author: commit.author, date: commit.date, linesChanged });
|
|
30
|
+
onProgress?.(i + 1, commits.length, title);
|
|
31
|
+
}
|
|
32
|
+
let githubIssuesImported = 0;
|
|
33
|
+
if (opts.github) {
|
|
34
|
+
const issues = await this.importGitHubIssues(cwd, tasks);
|
|
35
|
+
githubIssuesImported = issues.length;
|
|
36
|
+
tasks.push(...issues);
|
|
37
|
+
}
|
|
38
|
+
if (!opts.dryRun) {
|
|
39
|
+
this.persistTasks(tasks);
|
|
40
|
+
this.recalibrateFromImport();
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
commitsAnalyzed: commits.length,
|
|
44
|
+
tasksCreated: opts.dryRun ? 0 : tasks.length,
|
|
45
|
+
githubIssuesImported,
|
|
46
|
+
calibrationUpdated: !opts.dryRun,
|
|
47
|
+
tasks,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async verifyGit(cwd) {
|
|
51
|
+
try {
|
|
52
|
+
await execFileAsync('git', ['rev-parse', '--git-dir'], { cwd });
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
const error = err;
|
|
56
|
+
if (error.code === 'ENOENT')
|
|
57
|
+
throw new Error('git is not installed. Install git to use --from-existing.');
|
|
58
|
+
throw new Error('Not a git repository. Run from a directory with git history.');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async parseGitLog(cwd, since) {
|
|
62
|
+
const { stdout } = await execFileAsync('git', [
|
|
63
|
+
'log', '--no-merges', '--shortstat',
|
|
64
|
+
'--format=%x00%H%x01%s%x01%ae%x01%ai',
|
|
65
|
+
`--since=${since}`,
|
|
66
|
+
], { cwd, maxBuffer: 10 * 1024 * 1024 });
|
|
67
|
+
if (!stdout.trim())
|
|
68
|
+
return [];
|
|
69
|
+
const commits = [];
|
|
70
|
+
const entries = stdout.split('\x00').filter(e => e.trim());
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
const lines = entry.trim().split('\n');
|
|
73
|
+
const headerLine = lines[0];
|
|
74
|
+
if (!headerLine)
|
|
75
|
+
continue;
|
|
76
|
+
const fields = headerLine.split('\x01');
|
|
77
|
+
if (fields.length < 4)
|
|
78
|
+
continue;
|
|
79
|
+
const [hash, subject, author, date] = fields;
|
|
80
|
+
let insertions = 0;
|
|
81
|
+
let deletions = 0;
|
|
82
|
+
// shortstat appears on a separate line after the header
|
|
83
|
+
for (const line of lines.slice(1)) {
|
|
84
|
+
const statMatch = line.match(SHORTSTAT_RE);
|
|
85
|
+
if (statMatch) {
|
|
86
|
+
insertions = parseInt(statMatch[2] ?? '0', 10) || 0;
|
|
87
|
+
deletions = parseInt(statMatch[3] ?? '0', 10) || 0;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
commits.push({
|
|
92
|
+
hash: hash.slice(0, 8),
|
|
93
|
+
subject: this.sanitize(subject),
|
|
94
|
+
author,
|
|
95
|
+
date: date.split(' ')[0], // just the date part
|
|
96
|
+
insertions,
|
|
97
|
+
deletions,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return commits;
|
|
101
|
+
}
|
|
102
|
+
classifyCommit(subject) {
|
|
103
|
+
const match = subject.match(CONVENTIONAL_RE);
|
|
104
|
+
if (match) {
|
|
105
|
+
const type = COMMIT_TYPES[match[1]] ?? 'other';
|
|
106
|
+
const title = match[3].trim();
|
|
107
|
+
return { type, title };
|
|
108
|
+
}
|
|
109
|
+
return { type: 'other', title: subject };
|
|
110
|
+
}
|
|
111
|
+
estimateComplexity(linesChanged) {
|
|
112
|
+
if (linesChanged <= 50)
|
|
113
|
+
return 2;
|
|
114
|
+
if (linesChanged <= 150)
|
|
115
|
+
return 4;
|
|
116
|
+
if (linesChanged <= 400)
|
|
117
|
+
return 6;
|
|
118
|
+
return 8;
|
|
119
|
+
}
|
|
120
|
+
sanitize(text) {
|
|
121
|
+
// Strip control chars, truncate
|
|
122
|
+
return text.replace(/[\x00-\x1f]/g, '').trim().slice(0, 120);
|
|
123
|
+
}
|
|
124
|
+
persistTasks(tasks) {
|
|
125
|
+
const now = new Date().toISOString();
|
|
126
|
+
for (const task of tasks) {
|
|
127
|
+
const modelTier = getModelTier(task.complexity);
|
|
128
|
+
const status = task.author === 'github' ? 'backlog' : 'done';
|
|
129
|
+
this.store.run(`INSERT OR IGNORE INTO tasks (id, title, complexity, model_tier, status, why, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [task.id, task.title, task.complexity, modelTier, status, `Imported from ${task.type} commit`, task.date || now, now]);
|
|
130
|
+
const estimate = this.getCalibrationEstimate(task.complexity);
|
|
131
|
+
this.store.run('INSERT OR IGNORE INTO evm_metrics (task_id, planned_tokens, actual_tokens, updated_at) VALUES (?, ?, ?, ?)', [task.id, estimate, status === 'done' ? estimate : 0, now]);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
getCalibrationEstimate(complexity) {
|
|
135
|
+
const row = this.store.query('SELECT avg_tokens FROM calibration WHERE complexity_score = ?', [complexity]);
|
|
136
|
+
return row[0]?.avg_tokens ?? 800;
|
|
137
|
+
}
|
|
138
|
+
recalibrateFromImport() {
|
|
139
|
+
const buckets = this.store.query(`SELECT t.complexity, AVG(e.planned_tokens) as avg, COUNT(*) as cnt
|
|
140
|
+
FROM tasks t JOIN evm_metrics e ON t.id = e.task_id
|
|
141
|
+
GROUP BY t.complexity HAVING cnt >= 3`);
|
|
142
|
+
for (const b of buckets) {
|
|
143
|
+
this.store.run('UPDATE calibration SET avg_tokens = ?, sample_count = ?, updated_at = datetime("now") WHERE complexity_score = ?', [Math.round(b.avg), b.cnt, b.complexity]);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async importGitHubIssues(cwd, existingTasks) {
|
|
147
|
+
const dbTitles = this.store.query('SELECT title FROM tasks').map(r => r.title.toLowerCase());
|
|
148
|
+
const existingTitles = new Set([...existingTasks.map(t => t.title.toLowerCase()), ...dbTitles]);
|
|
149
|
+
const imported = [];
|
|
150
|
+
try {
|
|
151
|
+
const { stdout } = await execFileAsync('gh', [
|
|
152
|
+
'issue', 'list', '--state', 'open',
|
|
153
|
+
'--json', 'number,title,labels',
|
|
154
|
+
'--limit', '200',
|
|
155
|
+
], { cwd });
|
|
156
|
+
const issues = JSON.parse(stdout);
|
|
157
|
+
for (const issue of issues) {
|
|
158
|
+
if (existingTitles.has(issue.title.toLowerCase()))
|
|
159
|
+
continue;
|
|
160
|
+
const taskId = this.store.nextId('TASK');
|
|
161
|
+
const type = this.inferTypeFromLabels(issue.labels.map(l => l.name));
|
|
162
|
+
imported.push({
|
|
163
|
+
id: taskId, title: this.sanitize(issue.title), complexity: 3,
|
|
164
|
+
type, author: 'github', date: new Date().toISOString().split('T')[0], linesChanged: 0,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// gh not available or not authenticated — skip silently
|
|
170
|
+
}
|
|
171
|
+
return imported;
|
|
172
|
+
}
|
|
173
|
+
inferTypeFromLabels(labels) {
|
|
174
|
+
const lower = labels.map(l => l.toLowerCase());
|
|
175
|
+
if (lower.some(l => l.includes('bug')))
|
|
176
|
+
return 'bugfix';
|
|
177
|
+
if (lower.some(l => l.includes('enhancement') || l.includes('feature')))
|
|
178
|
+
return 'feature';
|
|
179
|
+
return 'backlog';
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=archaeology.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archaeology.js","sourceRoot":"","sources":["../../../src/cli/archaeology.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function main(argv: string[], cwd?: string): Promise<void>;
|