@speclife/core 0.1.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/adapters/claude-cli-adapter.d.ts +57 -0
- package/dist/adapters/claude-cli-adapter.d.ts.map +1 -0
- package/dist/adapters/claude-cli-adapter.js +161 -0
- package/dist/adapters/claude-cli-adapter.js.map +1 -0
- package/dist/adapters/claude-sdk-adapter.d.ts +49 -0
- package/dist/adapters/claude-sdk-adapter.d.ts.map +1 -0
- package/dist/adapters/claude-sdk-adapter.js +278 -0
- package/dist/adapters/claude-sdk-adapter.js.map +1 -0
- package/dist/adapters/cursor-adapter.d.ts +26 -0
- package/dist/adapters/cursor-adapter.d.ts.map +1 -0
- package/dist/adapters/cursor-adapter.js +54 -0
- package/dist/adapters/cursor-adapter.js.map +1 -0
- package/dist/adapters/environment-adapter.d.ts +153 -0
- package/dist/adapters/environment-adapter.d.ts.map +1 -0
- package/dist/adapters/environment-adapter.js +690 -0
- package/dist/adapters/environment-adapter.js.map +1 -0
- package/dist/adapters/git-adapter.d.ts +41 -0
- package/dist/adapters/git-adapter.d.ts.map +1 -0
- package/dist/adapters/git-adapter.js +95 -0
- package/dist/adapters/git-adapter.js.map +1 -0
- package/dist/adapters/github-adapter.d.ts +39 -0
- package/dist/adapters/github-adapter.d.ts.map +1 -0
- package/dist/adapters/github-adapter.js +129 -0
- package/dist/adapters/github-adapter.js.map +1 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +13 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/openspec-adapter.d.ts +36 -0
- package/dist/adapters/openspec-adapter.d.ts.map +1 -0
- package/dist/adapters/openspec-adapter.js +182 -0
- package/dist/adapters/openspec-adapter.js.map +1 -0
- package/dist/config.d.ts +60 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +112 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +105 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +28 -0
- package/dist/types.js.map +1 -0
- package/dist/workflows/implement.d.ts +28 -0
- package/dist/workflows/implement.d.ts.map +1 -0
- package/dist/workflows/implement.js +277 -0
- package/dist/workflows/implement.js.map +1 -0
- package/dist/workflows/index.d.ts +9 -0
- package/dist/workflows/index.d.ts.map +1 -0
- package/dist/workflows/index.js +9 -0
- package/dist/workflows/index.js.map +1 -0
- package/dist/workflows/init.d.ts +55 -0
- package/dist/workflows/init.d.ts.map +1 -0
- package/dist/workflows/init.js +195 -0
- package/dist/workflows/init.js.map +1 -0
- package/dist/workflows/merge.d.ts +40 -0
- package/dist/workflows/merge.d.ts.map +1 -0
- package/dist/workflows/merge.js +90 -0
- package/dist/workflows/merge.js.map +1 -0
- package/dist/workflows/status.d.ts +34 -0
- package/dist/workflows/status.d.ts.map +1 -0
- package/dist/workflows/status.js +53 -0
- package/dist/workflows/status.js.map +1 -0
- package/dist/workflows/submit.d.ts +44 -0
- package/dist/workflows/submit.d.ts.map +1 -0
- package/dist/workflows/submit.js +143 -0
- package/dist/workflows/submit.js.map +1 -0
- package/package.json +48 -0
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loading and validation
|
|
3
|
+
*/
|
|
4
|
+
import { type ImplementMode } from './types.js';
|
|
5
|
+
import type { BootstrapStrategy } from './adapters/environment-adapter.js';
|
|
6
|
+
/** Per-environment bootstrap configuration */
|
|
7
|
+
export interface EnvironmentBootstrapConfig {
|
|
8
|
+
/** Override strategy for this environment */
|
|
9
|
+
strategy?: BootstrapStrategy;
|
|
10
|
+
/** Whether to enable this environment (default: true) */
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Worktree configuration */
|
|
14
|
+
export interface WorktreeConfig {
|
|
15
|
+
/** Bootstrap configuration for environment setup */
|
|
16
|
+
bootstrap: {
|
|
17
|
+
/** Default bootstrap strategy (default: "symlink") */
|
|
18
|
+
strategy: BootstrapStrategy;
|
|
19
|
+
/** Per-environment overrides */
|
|
20
|
+
environments?: Record<string, EnvironmentBootstrapConfig>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** SpecLife configuration schema */
|
|
24
|
+
export interface SpecLifeConfig {
|
|
25
|
+
/** OpenSpec directory location (default: "openspec") */
|
|
26
|
+
specDir: string;
|
|
27
|
+
/** AI provider to use */
|
|
28
|
+
aiProvider: 'claude' | 'openai' | 'gemini';
|
|
29
|
+
/** AI model identifier */
|
|
30
|
+
aiModel: string;
|
|
31
|
+
/** Implementation mode for speclife_implement (default: "claude-cli") */
|
|
32
|
+
implementMode: ImplementMode;
|
|
33
|
+
/** GitHub configuration */
|
|
34
|
+
github: {
|
|
35
|
+
owner: string;
|
|
36
|
+
repo: string;
|
|
37
|
+
baseBranch: string;
|
|
38
|
+
};
|
|
39
|
+
/** Command to run tests */
|
|
40
|
+
testCommand: string;
|
|
41
|
+
/** Optional build command */
|
|
42
|
+
buildCommand?: string;
|
|
43
|
+
/** Files to always include in AI context */
|
|
44
|
+
contextFiles?: string[];
|
|
45
|
+
/** Worktree configuration */
|
|
46
|
+
worktree: WorktreeConfig;
|
|
47
|
+
/** Create draft PR during init (default: true) */
|
|
48
|
+
createDraftPR: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Load configuration from standard locations
|
|
52
|
+
*
|
|
53
|
+
* Search order:
|
|
54
|
+
* 1. .specliferc.yaml
|
|
55
|
+
* 2. .specliferc.json
|
|
56
|
+
* 3. speclife.config.js
|
|
57
|
+
* 4. package.json "speclife" key
|
|
58
|
+
*/
|
|
59
|
+
export declare function loadConfig(cwd?: string): Promise<SpecLifeConfig>;
|
|
60
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAA6B,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAE3E,8CAA8C;AAC9C,MAAM,WAAW,0BAA0B;IACzC,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,6BAA6B;AAC7B,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,SAAS,EAAE;QACT,sDAAsD;QACtD,QAAQ,EAAE,iBAAiB,CAAC;QAC5B,gCAAgC;QAChC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC;KAC3D,CAAC;CACH;AAED,oCAAoC;AACpC,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAEhB,yBAAyB;IACzB,UAAU,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAE3C,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAEhB,yEAAyE;IACzE,aAAa,EAAE,aAAa,CAAC;IAE7B,2BAA2B;IAC3B,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF,2BAA2B;IAC3B,WAAW,EAAE,MAAM,CAAC;IAEpB,6BAA6B;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB,6BAA6B;IAC7B,QAAQ,EAAE,cAAc,CAAC;IAEzB,kDAAkD;IAClD,aAAa,EAAE,OAAO,CAAC;CACxB;AAsBD;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAAC,GAAG,GAAE,MAAsB,GAAG,OAAO,CAAC,cAAc,CAAC,CAiDrF"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loading and validation
|
|
3
|
+
*/
|
|
4
|
+
import { cosmiconfig } from 'cosmiconfig';
|
|
5
|
+
import { SpecLifeError, ErrorCodes } from './types.js';
|
|
6
|
+
/** Default configuration values */
|
|
7
|
+
const defaults = {
|
|
8
|
+
specDir: 'openspec',
|
|
9
|
+
aiProvider: 'claude',
|
|
10
|
+
aiModel: 'claude-sonnet-4-20250514',
|
|
11
|
+
implementMode: 'claude-cli',
|
|
12
|
+
github: {
|
|
13
|
+
owner: '',
|
|
14
|
+
repo: '',
|
|
15
|
+
baseBranch: 'main',
|
|
16
|
+
},
|
|
17
|
+
testCommand: 'npm test',
|
|
18
|
+
worktree: {
|
|
19
|
+
bootstrap: {
|
|
20
|
+
strategy: 'symlink',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
createDraftPR: true,
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Load configuration from standard locations
|
|
27
|
+
*
|
|
28
|
+
* Search order:
|
|
29
|
+
* 1. .specliferc.yaml
|
|
30
|
+
* 2. .specliferc.json
|
|
31
|
+
* 3. speclife.config.js
|
|
32
|
+
* 4. package.json "speclife" key
|
|
33
|
+
*/
|
|
34
|
+
export async function loadConfig(cwd = process.cwd()) {
|
|
35
|
+
const explorer = cosmiconfig('speclife', {
|
|
36
|
+
searchPlaces: [
|
|
37
|
+
'.specliferc.yaml',
|
|
38
|
+
'.specliferc.yml',
|
|
39
|
+
'.specliferc.json',
|
|
40
|
+
'.specliferc',
|
|
41
|
+
'speclife.config.js',
|
|
42
|
+
'speclife.config.cjs',
|
|
43
|
+
'package.json',
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
const result = await explorer.search(cwd);
|
|
47
|
+
const fileConfig = result?.config ?? {};
|
|
48
|
+
// Merge with defaults
|
|
49
|
+
const config = {
|
|
50
|
+
...defaults,
|
|
51
|
+
...fileConfig,
|
|
52
|
+
github: {
|
|
53
|
+
...defaults.github,
|
|
54
|
+
...fileConfig.github,
|
|
55
|
+
},
|
|
56
|
+
worktree: {
|
|
57
|
+
...defaults.worktree,
|
|
58
|
+
...fileConfig.worktree,
|
|
59
|
+
bootstrap: {
|
|
60
|
+
...defaults.worktree?.bootstrap,
|
|
61
|
+
...fileConfig.worktree?.bootstrap,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
// Apply environment variable overrides
|
|
66
|
+
if (process.env.SPECLIFE_AI_PROVIDER) {
|
|
67
|
+
config.aiProvider = process.env.SPECLIFE_AI_PROVIDER;
|
|
68
|
+
}
|
|
69
|
+
if (process.env.SPECLIFE_AI_MODEL) {
|
|
70
|
+
config.aiModel = process.env.SPECLIFE_AI_MODEL;
|
|
71
|
+
}
|
|
72
|
+
if (process.env.SPECLIFE_IMPLEMENT_MODE) {
|
|
73
|
+
config.implementMode = process.env.SPECLIFE_IMPLEMENT_MODE;
|
|
74
|
+
}
|
|
75
|
+
// Validate required fields
|
|
76
|
+
validateConfig(config);
|
|
77
|
+
return config;
|
|
78
|
+
}
|
|
79
|
+
/** Valid bootstrap strategies */
|
|
80
|
+
const validBootstrapStrategies = ['symlink', 'install', 'none'];
|
|
81
|
+
/** Valid implementation modes */
|
|
82
|
+
const validImplementModes = ['claude-cli', 'claude-sdk', 'cursor'];
|
|
83
|
+
/**
|
|
84
|
+
* Validate configuration
|
|
85
|
+
*/
|
|
86
|
+
function validateConfig(config) {
|
|
87
|
+
if (!config.specDir) {
|
|
88
|
+
throw new SpecLifeError(ErrorCodes.CONFIG_INVALID, 'specDir is required', { field: 'specDir' });
|
|
89
|
+
}
|
|
90
|
+
if (!['claude', 'openai', 'gemini'].includes(config.aiProvider)) {
|
|
91
|
+
throw new SpecLifeError(ErrorCodes.CONFIG_INVALID, `Invalid aiProvider: ${config.aiProvider}. Must be one of: claude, openai, gemini`, { field: 'aiProvider', value: config.aiProvider });
|
|
92
|
+
}
|
|
93
|
+
// Validate implementMode
|
|
94
|
+
if (config.implementMode && !validImplementModes.includes(config.implementMode)) {
|
|
95
|
+
throw new SpecLifeError(ErrorCodes.CONFIG_INVALID, `Invalid implementMode: ${config.implementMode}. Must be one of: ${validImplementModes.join(', ')}`, { field: 'implementMode', value: config.implementMode });
|
|
96
|
+
}
|
|
97
|
+
// Validate worktree.bootstrap.strategy
|
|
98
|
+
if (config.worktree?.bootstrap?.strategy) {
|
|
99
|
+
if (!validBootstrapStrategies.includes(config.worktree.bootstrap.strategy)) {
|
|
100
|
+
throw new SpecLifeError(ErrorCodes.CONFIG_INVALID, `Invalid worktree.bootstrap.strategy: ${config.worktree.bootstrap.strategy}. Must be one of: ${validBootstrapStrategies.join(', ')}`, { field: 'worktree.bootstrap.strategy', value: config.worktree.bootstrap.strategy });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Validate per-environment strategies
|
|
104
|
+
if (config.worktree?.bootstrap?.environments) {
|
|
105
|
+
for (const [envName, envConfig] of Object.entries(config.worktree.bootstrap.environments)) {
|
|
106
|
+
if (envConfig.strategy && !validBootstrapStrategies.includes(envConfig.strategy)) {
|
|
107
|
+
throw new SpecLifeError(ErrorCodes.CONFIG_INVALID, `Invalid worktree.bootstrap.environments.${envName}.strategy: ${envConfig.strategy}. Must be one of: ${validBootstrapStrategies.join(', ')}`, { field: `worktree.bootstrap.environments.${envName}.strategy`, value: envConfig.strategy });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @speclife/core
|
|
3
|
+
*
|
|
4
|
+
* Core library for SpecLife - adapters and workflows for spec-driven development.
|
|
5
|
+
*/
|
|
6
|
+
export * from './types.js';
|
|
7
|
+
export { loadConfig, type SpecLifeConfig, type WorktreeConfig, type EnvironmentBootstrapConfig, } from './config.js';
|
|
8
|
+
export * from './adapters/index.js';
|
|
9
|
+
export * from './workflows/index.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,YAAY,CAAC;AAG3B,OAAO,EACL,UAAU,EACV,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,0BAA0B,GAChC,MAAM,aAAa,CAAC;AAGrB,cAAc,qBAAqB,CAAC;AAGpC,cAAc,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @speclife/core
|
|
3
|
+
*
|
|
4
|
+
* Core library for SpecLife - adapters and workflows for spec-driven development.
|
|
5
|
+
*/
|
|
6
|
+
// Re-export types
|
|
7
|
+
export * from './types.js';
|
|
8
|
+
// Re-export config
|
|
9
|
+
export { loadConfig, } from './config.js';
|
|
10
|
+
// Re-export adapters
|
|
11
|
+
export * from './adapters/index.js';
|
|
12
|
+
// Re-export workflows
|
|
13
|
+
export * from './workflows/index.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,kBAAkB;AAClB,cAAc,YAAY,CAAC;AAE3B,mBAAmB;AACnB,OAAO,EACL,UAAU,GAIX,MAAM,aAAa,CAAC;AAErB,qBAAqB;AACrB,cAAc,qBAAqB,CAAC;AAEpC,sBAAsB;AACtB,cAAc,sBAAsB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for SpecLife
|
|
3
|
+
*/
|
|
4
|
+
/** Change lifecycle states */
|
|
5
|
+
export type ChangeState = 'created' | 'implementing' | 'testing' | 'submitted' | 'merged';
|
|
6
|
+
/** A change proposal context */
|
|
7
|
+
export interface Change {
|
|
8
|
+
id: string;
|
|
9
|
+
branch: string;
|
|
10
|
+
state: ChangeState;
|
|
11
|
+
proposal: ChangeProposal;
|
|
12
|
+
tasks: ChangeTask[];
|
|
13
|
+
design?: string;
|
|
14
|
+
createdAt: Date;
|
|
15
|
+
}
|
|
16
|
+
/** Parsed proposal.md content */
|
|
17
|
+
export interface ChangeProposal {
|
|
18
|
+
why: string;
|
|
19
|
+
whatChanges: string[];
|
|
20
|
+
impact: {
|
|
21
|
+
affectedSpecs: string[];
|
|
22
|
+
affectedCode: string[];
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** A task from tasks.md */
|
|
26
|
+
export interface ChangeTask {
|
|
27
|
+
id: string;
|
|
28
|
+
content: string;
|
|
29
|
+
completed: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** Git status information */
|
|
32
|
+
export interface GitStatus {
|
|
33
|
+
current: string | null;
|
|
34
|
+
staged: string[];
|
|
35
|
+
unstaged: string[];
|
|
36
|
+
untracked: string[];
|
|
37
|
+
}
|
|
38
|
+
/** Pull request information */
|
|
39
|
+
export interface PullRequest {
|
|
40
|
+
number: number;
|
|
41
|
+
url: string;
|
|
42
|
+
title: string;
|
|
43
|
+
state: 'open' | 'closed' | 'merged';
|
|
44
|
+
mergeable: boolean | null;
|
|
45
|
+
draft: boolean;
|
|
46
|
+
}
|
|
47
|
+
/** Structured error for SpecLife operations */
|
|
48
|
+
export declare class SpecLifeError extends Error {
|
|
49
|
+
readonly code: string;
|
|
50
|
+
readonly context?: Record<string, unknown> | undefined;
|
|
51
|
+
constructor(code: string, message: string, context?: Record<string, unknown> | undefined);
|
|
52
|
+
}
|
|
53
|
+
/** Common error codes */
|
|
54
|
+
export declare const ErrorCodes: {
|
|
55
|
+
readonly CONFIG_INVALID: "CONFIG_INVALID";
|
|
56
|
+
readonly CHANGE_EXISTS: "CHANGE_EXISTS";
|
|
57
|
+
readonly CHANGE_NOT_FOUND: "CHANGE_NOT_FOUND";
|
|
58
|
+
readonly BRANCH_EXISTS: "BRANCH_EXISTS";
|
|
59
|
+
readonly NO_CHANGES: "NO_CHANGES";
|
|
60
|
+
readonly GITHUB_ERROR: "GITHUB_ERROR";
|
|
61
|
+
readonly AI_ERROR: "AI_ERROR";
|
|
62
|
+
readonly MISSING_TOKEN: "MISSING_TOKEN";
|
|
63
|
+
readonly CLI_NOT_FOUND: "CLI_NOT_FOUND";
|
|
64
|
+
readonly TEST_FAILED: "TEST_FAILED";
|
|
65
|
+
};
|
|
66
|
+
/** Implementation modes for speclife_implement */
|
|
67
|
+
export type ImplementMode = 'claude-cli' | 'claude-sdk' | 'cursor';
|
|
68
|
+
/** Options for the implement workflow */
|
|
69
|
+
export interface ImplementOptions {
|
|
70
|
+
/** Change ID to implement */
|
|
71
|
+
changeId: string;
|
|
72
|
+
/** Implementation mode (default: from config or 'claude-cli') */
|
|
73
|
+
mode?: ImplementMode;
|
|
74
|
+
/** Specific task ID to implement (e.g., "1.2") */
|
|
75
|
+
taskId?: string;
|
|
76
|
+
/** Return plan without executing */
|
|
77
|
+
dryRun?: boolean;
|
|
78
|
+
}
|
|
79
|
+
/** Result of the implement workflow */
|
|
80
|
+
export interface ImplementResult {
|
|
81
|
+
/** Mode used for implementation */
|
|
82
|
+
mode: ImplementMode;
|
|
83
|
+
/** Overall status */
|
|
84
|
+
status: 'success' | 'partial' | 'failed' | 'manual';
|
|
85
|
+
/** Human-readable output/summary */
|
|
86
|
+
output: string;
|
|
87
|
+
/** Tasks that were completed */
|
|
88
|
+
tasksCompleted: string[];
|
|
89
|
+
/** Tasks that failed (with reasons) */
|
|
90
|
+
tasksFailed?: Array<{
|
|
91
|
+
taskId: string;
|
|
92
|
+
reason: string;
|
|
93
|
+
}>;
|
|
94
|
+
/** For dry-run: the planned prompt/actions */
|
|
95
|
+
plan?: string;
|
|
96
|
+
}
|
|
97
|
+
/** Progress event emitted during long operations */
|
|
98
|
+
export interface ProgressEvent {
|
|
99
|
+
type: 'task_started' | 'task_completed' | 'file_written' | 'step_completed';
|
|
100
|
+
message: string;
|
|
101
|
+
data?: Record<string, unknown>;
|
|
102
|
+
}
|
|
103
|
+
/** Progress callback type */
|
|
104
|
+
export type ProgressCallback = (event: ProgressEvent) => void;
|
|
105
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for SpecLife
|
|
3
|
+
*/
|
|
4
|
+
/** Structured error for SpecLife operations */
|
|
5
|
+
export class SpecLifeError extends Error {
|
|
6
|
+
code;
|
|
7
|
+
context;
|
|
8
|
+
constructor(code, message, context) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.context = context;
|
|
12
|
+
this.name = 'SpecLifeError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** Common error codes */
|
|
16
|
+
export const ErrorCodes = {
|
|
17
|
+
CONFIG_INVALID: 'CONFIG_INVALID',
|
|
18
|
+
CHANGE_EXISTS: 'CHANGE_EXISTS',
|
|
19
|
+
CHANGE_NOT_FOUND: 'CHANGE_NOT_FOUND',
|
|
20
|
+
BRANCH_EXISTS: 'BRANCH_EXISTS',
|
|
21
|
+
NO_CHANGES: 'NO_CHANGES',
|
|
22
|
+
GITHUB_ERROR: 'GITHUB_ERROR',
|
|
23
|
+
AI_ERROR: 'AI_ERROR',
|
|
24
|
+
MISSING_TOKEN: 'MISSING_TOKEN',
|
|
25
|
+
CLI_NOT_FOUND: 'CLI_NOT_FOUND',
|
|
26
|
+
TEST_FAILED: 'TEST_FAILED',
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAwDH,+CAA+C;AAC/C,MAAM,OAAO,aAAc,SAAQ,KAAK;IAEpB;IAEA;IAHlB,YACkB,IAAY,EAC5B,OAAe,EACC,OAAiC;QAEjD,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,SAAI,GAAJ,IAAI,CAAQ;QAEZ,YAAO,GAAP,OAAO,CAA0B;QAGjD,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,yBAAyB;AACzB,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,cAAc,EAAE,gBAAgB;IAChC,aAAa,EAAE,eAAe;IAC9B,gBAAgB,EAAE,kBAAkB;IACpC,aAAa,EAAE,eAAe;IAC9B,UAAU,EAAE,YAAY;IACxB,YAAY,EAAE,cAAc;IAC5B,QAAQ,EAAE,UAAU;IACpB,aAAa,EAAE,eAAe;IAC9B,aAAa,EAAE,eAAe;IAC9B,WAAW,EAAE,aAAa;CAClB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implement workflow - AI-driven code implementation with multiple modes
|
|
3
|
+
*
|
|
4
|
+
* Supports three implementation modes:
|
|
5
|
+
* - claude-cli: Uses Claude CLI with MCP servers (primary)
|
|
6
|
+
* - claude-sdk: Direct Anthropic SDK with tool-use (fully automated)
|
|
7
|
+
* - cursor: Opens Cursor IDE for manual implementation
|
|
8
|
+
*/
|
|
9
|
+
import { type OpenSpecAdapter } from '../adapters/openspec-adapter.js';
|
|
10
|
+
import { type ClaudeCliAdapter } from '../adapters/claude-cli-adapter.js';
|
|
11
|
+
import { type ClaudeSdkAdapter } from '../adapters/claude-sdk-adapter.js';
|
|
12
|
+
import { type CursorAdapter } from '../adapters/cursor-adapter.js';
|
|
13
|
+
import { type SpecLifeConfig } from '../config.js';
|
|
14
|
+
import { type ImplementOptions, type ImplementResult, type ProgressCallback } from '../types.js';
|
|
15
|
+
/** Dependencies for the implement workflow */
|
|
16
|
+
interface ImplementDependencies {
|
|
17
|
+
openspec: OpenSpecAdapter;
|
|
18
|
+
config: SpecLifeConfig;
|
|
19
|
+
claudeCli?: ClaudeCliAdapter;
|
|
20
|
+
claudeSdk?: ClaudeSdkAdapter;
|
|
21
|
+
cursor?: CursorAdapter;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Implement a change using the specified mode
|
|
25
|
+
*/
|
|
26
|
+
export declare function implementWorkflow(options: ImplementOptions, deps: ImplementDependencies, onProgress?: ProgressCallback): Promise<ImplementResult>;
|
|
27
|
+
export type { ImplementDependencies };
|
|
28
|
+
//# sourceMappingURL=implement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"implement.d.ts","sourceRoot":"","sources":["../../src/workflows/implement.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACxF,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EAEtB,MAAM,aAAa,CAAC;AAErB,8CAA8C;AAC9C,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,eAAe,CAAC;IAC1B,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,qBAAqB,EAC3B,UAAU,CAAC,EAAE,gBAAgB,GAC5B,OAAO,CAAC,eAAe,CAAC,CA6E1B;AAgTD,YAAY,EAAE,qBAAqB,EAAE,CAAC"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implement workflow - AI-driven code implementation with multiple modes
|
|
3
|
+
*
|
|
4
|
+
* Supports three implementation modes:
|
|
5
|
+
* - claude-cli: Uses Claude CLI with MCP servers (primary)
|
|
6
|
+
* - claude-sdk: Direct Anthropic SDK with tool-use (fully automated)
|
|
7
|
+
* - cursor: Opens Cursor IDE for manual implementation
|
|
8
|
+
*/
|
|
9
|
+
import { readFile } from 'fs/promises';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { createClaudeCliAdapter, generateImplementationPrompt } from '../adapters/claude-cli-adapter.js';
|
|
12
|
+
import { createClaudeSdkAdapter, generateAgenticSystemPrompt } from '../adapters/claude-sdk-adapter.js';
|
|
13
|
+
import { createCursorAdapter } from '../adapters/cursor-adapter.js';
|
|
14
|
+
import { SpecLifeError, ErrorCodes, } from '../types.js';
|
|
15
|
+
/**
|
|
16
|
+
* Implement a change using the specified mode
|
|
17
|
+
*/
|
|
18
|
+
export async function implementWorkflow(options, deps, onProgress) {
|
|
19
|
+
const { changeId, taskId, dryRun = false } = options;
|
|
20
|
+
const mode = options.mode ?? deps.config.implementMode;
|
|
21
|
+
// Verify change exists
|
|
22
|
+
if (!await deps.openspec.changeExists(changeId)) {
|
|
23
|
+
throw new SpecLifeError(ErrorCodes.CHANGE_NOT_FOUND, `Change '${changeId}' not found`, { changeId });
|
|
24
|
+
}
|
|
25
|
+
onProgress?.({
|
|
26
|
+
type: 'step_completed',
|
|
27
|
+
message: `Starting implementation with mode: ${mode}`,
|
|
28
|
+
data: { mode, changeId },
|
|
29
|
+
});
|
|
30
|
+
// Read change context
|
|
31
|
+
const change = await deps.openspec.readChange(changeId);
|
|
32
|
+
const proposalContent = await readChangeFile(changeId, 'proposal.md', deps);
|
|
33
|
+
const tasksContent = await readChangeFile(changeId, 'tasks.md', deps);
|
|
34
|
+
const designContent = await readChangeFile(changeId, 'design.md', deps).catch(() => undefined);
|
|
35
|
+
// Filter tasks if taskId specified
|
|
36
|
+
let tasksToImplement = change.tasks.filter(t => !t.completed);
|
|
37
|
+
if (taskId) {
|
|
38
|
+
tasksToImplement = tasksToImplement.filter(t => t.id === taskId);
|
|
39
|
+
if (tasksToImplement.length === 0) {
|
|
40
|
+
throw new SpecLifeError(ErrorCodes.CHANGE_NOT_FOUND, `Task '${taskId}' not found or already completed`, { changeId, taskId });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
onProgress?.({
|
|
44
|
+
type: 'step_completed',
|
|
45
|
+
message: `Found ${tasksToImplement.length} task(s) to implement`,
|
|
46
|
+
data: { taskCount: tasksToImplement.length },
|
|
47
|
+
});
|
|
48
|
+
// Gather additional context files
|
|
49
|
+
const contextFiles = await gatherContextFiles(deps.config.contextFiles ?? [], process.cwd());
|
|
50
|
+
// Dispatch based on mode
|
|
51
|
+
switch (mode) {
|
|
52
|
+
case 'claude-cli':
|
|
53
|
+
return implementWithClaudeCli({ changeId, proposalContent, tasksContent, designContent, contextFiles, tasksToImplement, dryRun }, deps, onProgress);
|
|
54
|
+
case 'claude-sdk':
|
|
55
|
+
return implementWithClaudeSdk({ changeId, proposalContent, tasksContent, designContent, contextFiles, tasksToImplement, dryRun }, deps, onProgress);
|
|
56
|
+
case 'cursor':
|
|
57
|
+
return implementWithCursor({ changeId, dryRun }, deps, onProgress);
|
|
58
|
+
default:
|
|
59
|
+
throw new SpecLifeError(ErrorCodes.CONFIG_INVALID, `Unknown implementation mode: ${mode}`, { mode });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Implement using Claude CLI
|
|
64
|
+
*/
|
|
65
|
+
async function implementWithClaudeCli(context, deps, onProgress) {
|
|
66
|
+
const cli = deps.claudeCli ?? createClaudeCliAdapter();
|
|
67
|
+
// Check if CLI is available
|
|
68
|
+
if (!await cli.isAvailable()) {
|
|
69
|
+
throw new SpecLifeError(ErrorCodes.CLI_NOT_FOUND, 'Claude CLI not found. Install from https://docs.anthropic.com/claude-cli or use mode: claude-sdk', { mode: 'claude-cli' });
|
|
70
|
+
}
|
|
71
|
+
// Generate the implementation prompt
|
|
72
|
+
const prompt = generateImplementationPrompt({
|
|
73
|
+
changeId: context.changeId,
|
|
74
|
+
proposal: context.proposalContent,
|
|
75
|
+
tasks: context.tasksContent,
|
|
76
|
+
design: context.designContent,
|
|
77
|
+
contextFiles: context.contextFiles,
|
|
78
|
+
});
|
|
79
|
+
if (context.dryRun) {
|
|
80
|
+
return {
|
|
81
|
+
mode: 'claude-cli',
|
|
82
|
+
status: 'success',
|
|
83
|
+
output: 'Dry run - no changes made',
|
|
84
|
+
tasksCompleted: [],
|
|
85
|
+
plan: prompt,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
onProgress?.({
|
|
89
|
+
type: 'step_completed',
|
|
90
|
+
message: 'Invoking Claude CLI...',
|
|
91
|
+
});
|
|
92
|
+
// Run Claude CLI with streaming
|
|
93
|
+
const result = await cli.runStreaming(prompt, { cwd: process.cwd(), model: deps.config.aiModel }, (chunk) => {
|
|
94
|
+
onProgress?.({
|
|
95
|
+
type: 'step_completed',
|
|
96
|
+
message: chunk,
|
|
97
|
+
data: { streaming: true },
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
if (!result.success) {
|
|
101
|
+
return {
|
|
102
|
+
mode: 'claude-cli',
|
|
103
|
+
status: 'failed',
|
|
104
|
+
output: result.stderr || 'Claude CLI execution failed',
|
|
105
|
+
tasksCompleted: [],
|
|
106
|
+
tasksFailed: context.tasksToImplement.map(t => ({
|
|
107
|
+
taskId: t.id,
|
|
108
|
+
reason: 'CLI execution failed',
|
|
109
|
+
})),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// Parse output to detect completed tasks
|
|
113
|
+
// This is a simple heuristic - look for task IDs mentioned with completion indicators
|
|
114
|
+
const completedTasks = detectCompletedTasks(result.stdout, context.tasksToImplement);
|
|
115
|
+
return {
|
|
116
|
+
mode: 'claude-cli',
|
|
117
|
+
status: completedTasks.length === context.tasksToImplement.length ? 'success' : 'partial',
|
|
118
|
+
output: result.stdout,
|
|
119
|
+
tasksCompleted: completedTasks,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Implement using Claude SDK (fully automated)
|
|
124
|
+
*/
|
|
125
|
+
async function implementWithClaudeSdk(context, deps, onProgress) {
|
|
126
|
+
const sdk = deps.claudeSdk ?? createClaudeSdkAdapter();
|
|
127
|
+
// Check if API key is configured
|
|
128
|
+
if (!sdk.isConfigured()) {
|
|
129
|
+
throw new SpecLifeError(ErrorCodes.MISSING_TOKEN, 'ANTHROPIC_API_KEY environment variable required for claude-sdk mode', { mode: 'claude-sdk' });
|
|
130
|
+
}
|
|
131
|
+
// Generate prompts
|
|
132
|
+
const systemPrompt = generateAgenticSystemPrompt();
|
|
133
|
+
const userPrompt = generateImplementationPrompt({
|
|
134
|
+
changeId: context.changeId,
|
|
135
|
+
proposal: context.proposalContent,
|
|
136
|
+
tasks: context.tasksContent,
|
|
137
|
+
design: context.designContent,
|
|
138
|
+
contextFiles: context.contextFiles,
|
|
139
|
+
});
|
|
140
|
+
if (context.dryRun) {
|
|
141
|
+
return {
|
|
142
|
+
mode: 'claude-sdk',
|
|
143
|
+
status: 'success',
|
|
144
|
+
output: 'Dry run - no changes made',
|
|
145
|
+
tasksCompleted: [],
|
|
146
|
+
plan: `System prompt:\n${systemPrompt}\n\nUser prompt:\n${userPrompt}`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
onProgress?.({
|
|
150
|
+
type: 'step_completed',
|
|
151
|
+
message: 'Starting agentic implementation loop...',
|
|
152
|
+
});
|
|
153
|
+
// Run the agentic loop
|
|
154
|
+
const result = await sdk.runAgenticLoop(systemPrompt, userPrompt, {
|
|
155
|
+
cwd: process.cwd(),
|
|
156
|
+
model: deps.config.aiModel,
|
|
157
|
+
maxIterations: 50,
|
|
158
|
+
onProgress,
|
|
159
|
+
});
|
|
160
|
+
if (!result.success) {
|
|
161
|
+
return {
|
|
162
|
+
mode: 'claude-sdk',
|
|
163
|
+
status: 'failed',
|
|
164
|
+
output: result.errors.join('\n'),
|
|
165
|
+
tasksCompleted: [],
|
|
166
|
+
tasksFailed: context.tasksToImplement.map(t => ({
|
|
167
|
+
taskId: t.id,
|
|
168
|
+
reason: result.errors[0] ?? 'Unknown error',
|
|
169
|
+
})),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
// Detect completed tasks from the final response
|
|
173
|
+
const completedTasks = detectCompletedTasks(result.finalResponse, context.tasksToImplement);
|
|
174
|
+
onProgress?.({
|
|
175
|
+
type: 'step_completed',
|
|
176
|
+
message: `Implementation complete. ${result.iterations} iterations, ${result.filesModified.length} files modified`,
|
|
177
|
+
data: { iterations: result.iterations, filesModified: result.filesModified },
|
|
178
|
+
});
|
|
179
|
+
return {
|
|
180
|
+
mode: 'claude-sdk',
|
|
181
|
+
status: completedTasks.length === context.tasksToImplement.length ? 'success' : 'partial',
|
|
182
|
+
output: result.finalResponse,
|
|
183
|
+
tasksCompleted: completedTasks,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Implement using Cursor IDE (manual)
|
|
188
|
+
*/
|
|
189
|
+
async function implementWithCursor(context, deps, onProgress) {
|
|
190
|
+
const cursor = deps.cursor ?? createCursorAdapter();
|
|
191
|
+
// Check if Cursor is available
|
|
192
|
+
if (!await cursor.isAvailable()) {
|
|
193
|
+
throw new SpecLifeError(ErrorCodes.CLI_NOT_FOUND, "Cursor CLI not found. Install Cursor and ensure 'cursor' command is available", { mode: 'cursor' });
|
|
194
|
+
}
|
|
195
|
+
const worktreePath = process.cwd();
|
|
196
|
+
if (context.dryRun) {
|
|
197
|
+
return {
|
|
198
|
+
mode: 'cursor',
|
|
199
|
+
status: 'success',
|
|
200
|
+
output: 'Dry run - no changes made',
|
|
201
|
+
tasksCompleted: [],
|
|
202
|
+
plan: `Would open Cursor at: ${worktreePath}`,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
onProgress?.({
|
|
206
|
+
type: 'step_completed',
|
|
207
|
+
message: 'Opening Cursor IDE...',
|
|
208
|
+
});
|
|
209
|
+
// Open Cursor
|
|
210
|
+
const result = await cursor.open(worktreePath);
|
|
211
|
+
if (!result.success) {
|
|
212
|
+
return {
|
|
213
|
+
mode: 'cursor',
|
|
214
|
+
status: 'failed',
|
|
215
|
+
output: result.message,
|
|
216
|
+
tasksCompleted: [],
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
mode: 'cursor',
|
|
221
|
+
status: 'manual',
|
|
222
|
+
output: `Cursor opened at: ${worktreePath}\n\nImplement the tasks manually using Cursor's AI features, then mark them complete in tasks.md.`,
|
|
223
|
+
tasksCompleted: [],
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Read a file from the change directory
|
|
228
|
+
*/
|
|
229
|
+
async function readChangeFile(changeId, filename, deps) {
|
|
230
|
+
const changePath = join(process.cwd(), deps.config.specDir, 'changes', changeId, filename);
|
|
231
|
+
return readFile(changePath, 'utf-8');
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Gather context files from the project
|
|
235
|
+
*/
|
|
236
|
+
async function gatherContextFiles(paths, projectRoot) {
|
|
237
|
+
const files = [];
|
|
238
|
+
for (const path of paths) {
|
|
239
|
+
try {
|
|
240
|
+
const fullPath = join(projectRoot, path);
|
|
241
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
242
|
+
files.push({ path, content });
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
// Skip files that don't exist or can't be read
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return files;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Detect which tasks were completed based on output text
|
|
252
|
+
* This is a heuristic that looks for task IDs with completion indicators
|
|
253
|
+
*/
|
|
254
|
+
function detectCompletedTasks(output, tasks) {
|
|
255
|
+
const completed = [];
|
|
256
|
+
// Look for patterns like "completed task 1.1", "✓ 1.1", "task 1.1 done", etc.
|
|
257
|
+
const completionPatterns = [
|
|
258
|
+
/completed?\s+(?:task\s+)?(\d+\.\d+)/gi,
|
|
259
|
+
/✓\s*(\d+\.\d+)/g,
|
|
260
|
+
/(?:task\s+)?(\d+\.\d+)\s+(?:done|complete|finished)/gi,
|
|
261
|
+
/\[x\]\s*(\d+\.\d+)/gi,
|
|
262
|
+
];
|
|
263
|
+
for (const task of tasks) {
|
|
264
|
+
for (const pattern of completionPatterns) {
|
|
265
|
+
pattern.lastIndex = 0; // Reset regex state
|
|
266
|
+
let match;
|
|
267
|
+
while ((match = pattern.exec(output)) !== null) {
|
|
268
|
+
if (match[1] === task.id) {
|
|
269
|
+
completed.push(task.id);
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return [...new Set(completed)]; // Remove duplicates
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=implement.js.map
|