kodu 1.2.0 → 2.0.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/AGENTS.md +36 -68
- package/README.md +97 -96
- package/dist/package.json +1 -2
- package/dist/src/app.module.js +0 -8
- package/dist/src/app.module.js.map +1 -1
- package/dist/src/commands/init/init.command.d.ts +2 -9
- package/dist/src/commands/init/init.command.js +15 -241
- package/dist/src/commands/init/init.command.js.map +1 -1
- package/dist/src/commands/pack/pack.command.d.ts +7 -0
- package/dist/src/commands/pack/pack.command.js +59 -3
- package/dist/src/commands/pack/pack.command.js.map +1 -1
- package/dist/src/core/config/config.schema.d.ts +0 -46
- package/dist/src/core/config/config.schema.js +1 -51
- package/dist/src/core/config/config.schema.js.map +1 -1
- package/dist/src/core/config/config.service.js +2 -2
- package/dist/src/core/config/config.service.js.map +1 -1
- package/dist/src/core/config/prompt.service.d.ts +1 -4
- package/dist/src/core/config/prompt.service.js +4 -17
- package/dist/src/core/config/prompt.service.js.map +1 -1
- package/dist/src/shared/constants.d.ts +0 -4
- package/dist/src/shared/constants.js +1 -5
- package/dist/src/shared/constants.js.map +1 -1
- package/dist/src/shared/git/git.module.js +0 -2
- package/dist/src/shared/git/git.module.js.map +1 -1
- package/dist/src/shared/git/git.service.d.ts +0 -8
- package/dist/src/shared/git/git.service.js +2 -34
- package/dist/src/shared/git/git.service.js.map +1 -1
- package/dist/src/shared/tokenizer/tokenizer.module.js +0 -2
- package/dist/src/shared/tokenizer/tokenizer.module.js.map +1 -1
- package/dist/src/shared/tokenizer/tokenizer.service.d.ts +0 -6
- package/dist/src/shared/tokenizer/tokenizer.service.js +8 -38
- package/dist/src/shared/tokenizer/tokenizer.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/kodu.schema.json +0 -139
- package/package.json +1 -2
- package/src/app.module.ts +0 -8
- package/src/commands/init/init.command.ts +15 -310
- package/src/commands/pack/pack.command.ts +56 -3
- package/src/core/config/config.schema.ts +1 -68
- package/src/core/config/config.service.ts +2 -2
- package/src/core/config/prompt.service.ts +4 -26
- package/src/shared/constants.ts +0 -4
- package/src/shared/git/git.module.ts +0 -2
- package/src/shared/git/git.service.ts +1 -33
- package/src/shared/tokenizer/tokenizer.module.ts +0 -2
- package/src/shared/tokenizer/tokenizer.service.ts +9 -39
- package/.kodu/prompts/.keep +0 -0
- package/.kodu/prompts/commit.md +0 -9
- package/.kodu/prompts/pack.md +0 -7
- package/.kodu/prompts/review-bug.md +0 -6
- package/.kodu/prompts/review-security.md +0 -6
- package/.kodu/prompts/review-style.md +0 -6
- package/.opencode/command/openspec-apply.md +0 -24
- package/.opencode/command/openspec-archive.md +0 -27
- package/.opencode/command/openspec-proposal.md +0 -29
- package/.opencode/skills/kodu-ops/SKILL.md +0 -184
- package/dist/src/commands/commit/commit.command.d.ts +0 -18
- package/dist/src/commands/commit/commit.command.js +0 -149
- package/dist/src/commands/commit/commit.command.js.map +0 -1
- package/dist/src/commands/commit/commit.module.d.ts +0 -2
- package/dist/src/commands/commit/commit.module.js +0 -25
- package/dist/src/commands/commit/commit.module.js.map +0 -1
- package/dist/src/commands/ops/ops.command.d.ts +0 -4
- package/dist/src/commands/ops/ops.command.js +0 -39
- package/dist/src/commands/ops/ops.command.js.map +0 -1
- package/dist/src/commands/ops/ops.module.d.ts +0 -2
- package/dist/src/commands/ops/ops.module.js +0 -33
- package/dist/src/commands/ops/ops.module.js.map +0 -1
- package/dist/src/commands/ops/ops.types.d.ts +0 -13
- package/dist/src/commands/ops/ops.types.js +0 -12
- package/dist/src/commands/ops/ops.types.js.map +0 -1
- package/dist/src/commands/ops/ops.utils.d.ts +0 -13
- package/dist/src/commands/ops/ops.utils.js +0 -121
- package/dist/src/commands/ops/ops.utils.js.map +0 -1
- package/dist/src/commands/ops/subcommands/ops-env.command.d.ts +0 -24
- package/dist/src/commands/ops/subcommands/ops-env.command.js +0 -156
- package/dist/src/commands/ops/subcommands/ops-env.command.js.map +0 -1
- package/dist/src/commands/ops/subcommands/ops-routes.command.d.ts +0 -22
- package/dist/src/commands/ops/subcommands/ops-routes.command.js +0 -203
- package/dist/src/commands/ops/subcommands/ops-routes.command.js.map +0 -1
- package/dist/src/commands/ops/subcommands/ops-service.command.d.ts +0 -22
- package/dist/src/commands/ops/subcommands/ops-service.command.js +0 -169
- package/dist/src/commands/ops/subcommands/ops-service.command.js.map +0 -1
- package/dist/src/commands/ops/subcommands/ops-sysinfo.command.d.ts +0 -14
- package/dist/src/commands/ops/subcommands/ops-sysinfo.command.js +0 -75
- package/dist/src/commands/ops/subcommands/ops-sysinfo.command.js.map +0 -1
- package/dist/src/commands/review/review.command.d.ts +0 -26
- package/dist/src/commands/review/review.command.js +0 -205
- package/dist/src/commands/review/review.command.js.map +0 -1
- package/dist/src/commands/review/review.module.d.ts +0 -2
- package/dist/src/commands/review/review.module.js +0 -26
- package/dist/src/commands/review/review.module.js.map +0 -1
- package/dist/src/core/config/default-prompts.d.ts +0 -9
- package/dist/src/core/config/default-prompts.js +0 -49
- package/dist/src/core/config/default-prompts.js.map +0 -1
- package/dist/src/shared/ai/ai.module.d.ts +0 -2
- package/dist/src/shared/ai/ai.module.js +0 -23
- package/dist/src/shared/ai/ai.module.js.map +0 -1
- package/dist/src/shared/ai/ai.service.d.ts +0 -22
- package/dist/src/shared/ai/ai.service.js +0 -164
- package/dist/src/shared/ai/ai.service.js.map +0 -1
- package/dist/src/shared/ssh/ssh.module.d.ts +0 -2
- package/dist/src/shared/ssh/ssh.module.js +0 -21
- package/dist/src/shared/ssh/ssh.module.js.map +0 -1
- package/dist/src/shared/ssh/ssh.service.d.ts +0 -11
- package/dist/src/shared/ssh/ssh.service.js +0 -53
- package/dist/src/shared/ssh/ssh.service.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/commands/commit/commit.command.ts +0 -139
- package/src/commands/commit/commit.module.ts +0 -12
- package/src/commands/ops/ops.command.ts +0 -30
- package/src/commands/ops/ops.module.ts +0 -20
- package/src/commands/ops/ops.types.ts +0 -24
- package/src/commands/ops/ops.utils.ts +0 -160
- package/src/commands/ops/subcommands/ops-env.command.ts +0 -165
- package/src/commands/ops/subcommands/ops-routes.command.ts +0 -221
- package/src/commands/ops/subcommands/ops-service.command.ts +0 -190
- package/src/commands/ops/subcommands/ops-sysinfo.command.ts +0 -77
- package/src/commands/review/review.command.ts +0 -199
- package/src/commands/review/review.module.ts +0 -13
- package/src/core/config/default-prompts.ts +0 -53
- package/src/shared/ai/ai.module.ts +0 -10
- package/src/shared/ai/ai.service.ts +0 -216
- package/src/shared/ssh/ssh.module.ts +0 -8
- package/src/shared/ssh/ssh.service.ts +0 -61
|
@@ -1,43 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
3
|
-
DEFAULT_COMMIT_TOKENS,
|
|
4
|
-
DEFAULT_LLM_MODEL,
|
|
5
|
-
DEFAULT_REVIEW_TOKENS,
|
|
6
|
-
} from '../../shared/constants';
|
|
7
|
-
|
|
8
|
-
// Model ID format: provider/model-name (e.g., "openai/gpt-4o", "anthropic/claude-4-5-sonnet")
|
|
9
|
-
const modelIdSchema = z.string().regex(/^[a-zA-Z0-9-_]+\/[a-zA-Z0-9-_.]+$/, {
|
|
10
|
-
message:
|
|
11
|
-
"Model must be in format 'provider/model-name' (e.g., 'openai/gpt-4o')",
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
const llmCommandSettingsSchema = z
|
|
15
|
-
.object({
|
|
16
|
-
maxOutputTokens: z.number().int().positive().optional(),
|
|
17
|
-
})
|
|
18
|
-
.passthrough();
|
|
19
|
-
|
|
20
|
-
const llmCommandSchema = z.object({
|
|
21
|
-
modelSettings: llmCommandSettingsSchema.optional(),
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const createDefaultCommandSettings = () => ({
|
|
25
|
-
commit: { modelSettings: { maxOutputTokens: DEFAULT_COMMIT_TOKENS } },
|
|
26
|
-
review: { modelSettings: { maxOutputTokens: DEFAULT_REVIEW_TOKENS } },
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
const llmCommandsSchema = z
|
|
30
|
-
.object({
|
|
31
|
-
commit: llmCommandSchema.optional(),
|
|
32
|
-
review: llmCommandSchema.optional(),
|
|
33
|
-
})
|
|
34
|
-
.default(() => createDefaultCommandSettings());
|
|
35
|
-
|
|
36
|
-
const llmSchema = z.object({
|
|
37
|
-
model: modelIdSchema.default(`openai/${DEFAULT_LLM_MODEL}`),
|
|
38
|
-
apiKeyEnv: z.string().default('OPENAI_API_KEY'),
|
|
39
|
-
commands: llmCommandsSchema.optional(),
|
|
40
|
-
});
|
|
41
2
|
|
|
42
3
|
const cleanerSchema = z.object({
|
|
43
4
|
whitelist: z.array(z.string()).default(['//!']),
|
|
@@ -63,40 +24,14 @@ const packerSchema = z.object({
|
|
|
63
24
|
contentBasedBinaryDetection: z.boolean().default(false),
|
|
64
25
|
});
|
|
65
26
|
|
|
66
|
-
const promptSourceSchema = z.string();
|
|
67
|
-
|
|
68
27
|
const promptsSchema = z
|
|
69
28
|
.object({
|
|
70
|
-
|
|
71
|
-
commit: promptSourceSchema.optional(),
|
|
72
|
-
pack: promptSourceSchema.optional(),
|
|
29
|
+
pack: z.string().optional(),
|
|
73
30
|
})
|
|
74
31
|
.optional();
|
|
75
32
|
|
|
76
|
-
const serverPathsSchema = z
|
|
77
|
-
.object({
|
|
78
|
-
apps: z.string().default('/var/agent-apps'),
|
|
79
|
-
caddy: z.string().optional(),
|
|
80
|
-
})
|
|
81
|
-
.optional();
|
|
82
|
-
|
|
83
|
-
const serverConfigSchema = z.object({
|
|
84
|
-
host: z.string(),
|
|
85
|
-
port: z.number().default(22),
|
|
86
|
-
user: z.string(),
|
|
87
|
-
sshKeyPath: z.string(),
|
|
88
|
-
description: z.string().optional(),
|
|
89
|
-
paths: serverPathsSchema,
|
|
90
|
-
env: z.record(z.string(), z.string()).optional(),
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const opsSchema = z.object({
|
|
94
|
-
servers: z.record(z.string(), serverConfigSchema),
|
|
95
|
-
});
|
|
96
|
-
|
|
97
33
|
export const configSchema = z.object({
|
|
98
34
|
$schema: z.string().optional(),
|
|
99
|
-
llm: llmSchema.optional(),
|
|
100
35
|
cleaner: cleanerSchema.default({
|
|
101
36
|
whitelist: ['//!'],
|
|
102
37
|
keepJSDoc: true,
|
|
@@ -118,8 +53,6 @@ export const configSchema = z.object({
|
|
|
118
53
|
contentBasedBinaryDetection: false,
|
|
119
54
|
}),
|
|
120
55
|
prompts: promptsSchema,
|
|
121
|
-
ops: opsSchema.optional(),
|
|
122
56
|
});
|
|
123
57
|
|
|
124
58
|
export type KoduConfig = z.infer<typeof configSchema>;
|
|
125
|
-
export type ServerConfig = z.infer<typeof serverConfigSchema>;
|
|
@@ -21,14 +21,14 @@ export class ConfigService {
|
|
|
21
21
|
|
|
22
22
|
if (!result || result.isEmpty || !result.config) {
|
|
23
23
|
this.terminate(
|
|
24
|
-
'kodu.json
|
|
24
|
+
'kodu.json not found. Create it in the project root to configure kodu.',
|
|
25
25
|
);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const parsed = configSchema.safeParse(result.config);
|
|
29
29
|
|
|
30
30
|
if (!parsed.success) {
|
|
31
|
-
console.error(pc.red('kodu.json
|
|
31
|
+
console.error(pc.red('kodu.json is invalid:'));
|
|
32
32
|
parsed.error.issues.forEach((issue) => {
|
|
33
33
|
const path = issue.path.join('.') || '(root)';
|
|
34
34
|
console.error(pc.red(`- ${path}: ${issue.message}`));
|
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { Injectable } from '@nestjs/common';
|
|
4
|
-
import { replacePromptVariables } from './default-prompts';
|
|
5
|
-
|
|
6
|
-
type Variables = Record<string, string | number>;
|
|
7
4
|
|
|
8
5
|
@Injectable()
|
|
9
6
|
export class PromptService {
|
|
10
7
|
private readonly cache = new Map<string, string>();
|
|
11
8
|
|
|
12
|
-
async load(source: string
|
|
13
|
-
|
|
14
|
-
return variables
|
|
15
|
-
? replacePromptVariables(raw, this.normalize(variables))
|
|
16
|
-
: raw;
|
|
9
|
+
async load(source: string): Promise<string> {
|
|
10
|
+
return this.readSource(source);
|
|
17
11
|
}
|
|
18
12
|
|
|
19
13
|
async loadFromPromptsDir(name: string): Promise<string> {
|
|
@@ -66,29 +60,13 @@ export class PromptService {
|
|
|
66
60
|
}
|
|
67
61
|
const hasPathSegments = value.includes('/') || value.includes('\\');
|
|
68
62
|
const hasExtension = path.extname(value) !== '';
|
|
69
|
-
|
|
70
63
|
return value.trim().length > 0 && !hasPathSegments && !hasExtension;
|
|
71
64
|
}
|
|
72
65
|
|
|
73
66
|
private buildCandidates(name: string): string[] {
|
|
74
67
|
const names = path.extname(name) ? [name] : [`${name}.md`, `${name}.txt`];
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const candidates: string[] = [];
|
|
79
|
-
for (const root of roots) {
|
|
80
|
-
for (const variant of names) {
|
|
81
|
-
candidates.push(path.join(root, variant));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return candidates;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
private normalize(variables: Variables): Record<string, string> {
|
|
89
|
-
return Object.fromEntries(
|
|
90
|
-
Object.entries(variables).map(([key, value]) => [key, value.toString()]),
|
|
91
|
-
);
|
|
68
|
+
const root = path.join(process.cwd(), '.kodu', 'prompts');
|
|
69
|
+
return names.map((variant) => path.join(root, variant));
|
|
92
70
|
}
|
|
93
71
|
|
|
94
72
|
private async exists(target: string): Promise<boolean> {
|
package/src/shared/constants.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
export const MAX_FILE_SIZE_BYTES = 1024 * 1024; // 1 MB
|
|
2
|
-
export const WARNING_TOKEN_THRESHOLD = 12_000;
|
|
3
|
-
export const DEFAULT_COMMIT_TOKENS = 1_500;
|
|
4
|
-
export const DEFAULT_REVIEW_TOKENS = 5_000;
|
|
5
|
-
export const DEFAULT_LLM_MODEL = 'gpt-4o';
|
|
6
2
|
export const DEFAULT_PRICE_PER_MILLION = 5;
|
|
7
3
|
|
|
8
4
|
const BINARY_EXTENSION_LIST = [
|
|
@@ -1,38 +1,20 @@
|
|
|
1
1
|
import { Injectable } from '@nestjs/common';
|
|
2
2
|
import { execa } from 'execa';
|
|
3
|
-
import { ConfigService } from '../../core/config/config.service';
|
|
4
|
-
|
|
5
|
-
const EXCLUDE_PREFIX = ':(exclude)';
|
|
6
3
|
|
|
7
4
|
@Injectable()
|
|
8
5
|
export class GitService {
|
|
9
|
-
constructor(private readonly configService: ConfigService) {}
|
|
10
|
-
|
|
11
6
|
async ensureRepo(): Promise<void> {
|
|
12
7
|
try {
|
|
13
8
|
await execa('git', ['rev-parse', '--is-inside-work-tree']);
|
|
14
9
|
} catch (error) {
|
|
15
10
|
const message =
|
|
16
|
-
error instanceof Error && '
|
|
11
|
+
error instanceof Error && 'stderr' in error
|
|
17
12
|
? String((error as { stderr?: string }).stderr ?? error.message)
|
|
18
13
|
: 'Git repository not found. Initialize git before running the command.';
|
|
19
14
|
throw new Error(message);
|
|
20
15
|
}
|
|
21
16
|
}
|
|
22
17
|
|
|
23
|
-
async hasStagedChanges(): Promise<boolean> {
|
|
24
|
-
const { stdout } = await execa('git', ['diff', '--staged', '--name-only']);
|
|
25
|
-
return stdout.trim().length > 0;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async getStagedDiff(): Promise<string> {
|
|
29
|
-
await this.ensureRepo();
|
|
30
|
-
const excludeArgs = this.buildExcludeArgs();
|
|
31
|
-
const args = ['diff', '--staged', '--unified=3', '--', '.', ...excludeArgs];
|
|
32
|
-
const { stdout } = await execa('git', args);
|
|
33
|
-
return stdout;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
18
|
async getChangedFiles(): Promise<string[]> {
|
|
37
19
|
await this.ensureRepo();
|
|
38
20
|
const changed = new Set<string>();
|
|
@@ -52,18 +34,4 @@ export class GitService {
|
|
|
52
34
|
await load(['ls-files', '--others', '--exclude-standard']);
|
|
53
35
|
return [...changed].sort();
|
|
54
36
|
}
|
|
55
|
-
|
|
56
|
-
async getStatusShort(): Promise<string> {
|
|
57
|
-
const { stdout } = await execa('git', ['status', '--short']);
|
|
58
|
-
return stdout.trim();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async commit(message: string): Promise<void> {
|
|
62
|
-
await execa('git', ['commit', '-m', message]);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
private buildExcludeArgs(): string[] {
|
|
66
|
-
const ignore = this.configService.getConfig().packer.ignore ?? [];
|
|
67
|
-
return ignore.map((pattern) => `${EXCLUDE_PREFIX}${pattern}`);
|
|
68
|
-
}
|
|
69
37
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { Module } from '@nestjs/common';
|
|
2
|
-
import { ConfigModule } from '../../core/config/config.module';
|
|
3
2
|
import { TokenizerService } from './tokenizer.service';
|
|
4
3
|
|
|
5
4
|
@Module({
|
|
6
|
-
imports: [ConfigModule],
|
|
7
5
|
providers: [TokenizerService],
|
|
8
6
|
exports: [TokenizerService],
|
|
9
7
|
})
|
|
@@ -1,60 +1,30 @@
|
|
|
1
1
|
import { Injectable } from '@nestjs/common';
|
|
2
|
-
import { encodingForModel, getEncoding
|
|
3
|
-
import {
|
|
4
|
-
import { DEFAULT_LLM_MODEL, DEFAULT_PRICE_PER_MILLION } from '../constants';
|
|
2
|
+
import { encodingForModel, getEncoding } from 'js-tiktoken';
|
|
3
|
+
import { DEFAULT_PRICE_PER_MILLION } from '../constants';
|
|
5
4
|
|
|
6
5
|
type TokenEstimate = {
|
|
7
6
|
tokens: number;
|
|
8
7
|
usdEstimate: number;
|
|
9
8
|
};
|
|
10
9
|
|
|
11
|
-
const PRICE_PER_MILLION: Record<string, number> = {
|
|
12
|
-
'gpt-4o': 10,
|
|
13
|
-
'gpt-5-mini': 2.5,
|
|
14
|
-
};
|
|
15
|
-
|
|
16
10
|
@Injectable()
|
|
17
11
|
export class TokenizerService {
|
|
18
|
-
private encoder?: ReturnType<typeof
|
|
19
|
-
|
|
20
|
-
constructor(private readonly configService: ConfigService) {}
|
|
12
|
+
private encoder?: ReturnType<typeof getEncoding>;
|
|
21
13
|
|
|
22
14
|
count(text: string): TokenEstimate {
|
|
23
15
|
const tokens = this.getEncoder().encode(text).length;
|
|
24
|
-
const
|
|
25
|
-
const usdEstimate = price > 0 ? (tokens / 1_000_000) * price : 0;
|
|
26
|
-
|
|
16
|
+
const usdEstimate = (tokens / 1_000_000) * DEFAULT_PRICE_PER_MILLION;
|
|
27
17
|
return { tokens, usdEstimate };
|
|
28
18
|
}
|
|
29
19
|
|
|
30
|
-
private getPricePerMillion(): number {
|
|
31
|
-
const config = this.configService.getConfig();
|
|
32
|
-
const model = config.llm?.model ?? `openai/${DEFAULT_LLM_MODEL}`;
|
|
33
|
-
const key = this.normalizeModelKey(model);
|
|
34
|
-
return PRICE_PER_MILLION[key] ?? DEFAULT_PRICE_PER_MILLION;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
private createEncoder() {
|
|
38
|
-
try {
|
|
39
|
-
return encodingForModel(DEFAULT_LLM_MODEL as TiktokenModel);
|
|
40
|
-
} catch {
|
|
41
|
-
return getEncoding('o200k_base');
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
20
|
private getEncoder() {
|
|
46
21
|
if (!this.encoder) {
|
|
47
|
-
|
|
22
|
+
try {
|
|
23
|
+
this.encoder = encodingForModel('gpt-4o');
|
|
24
|
+
} catch {
|
|
25
|
+
this.encoder = getEncoding('o200k_base');
|
|
26
|
+
}
|
|
48
27
|
}
|
|
49
|
-
|
|
50
28
|
return this.encoder;
|
|
51
29
|
}
|
|
52
|
-
|
|
53
|
-
private normalizeModelKey(model: string): string {
|
|
54
|
-
const lower = model.toLowerCase();
|
|
55
|
-
if (lower.includes('/')) {
|
|
56
|
-
return lower.split('/').pop() ?? lower;
|
|
57
|
-
}
|
|
58
|
-
return lower;
|
|
59
|
-
}
|
|
60
30
|
}
|
package/.kodu/prompts/.keep
DELETED
|
File without changes
|
package/.kodu/prompts/commit.md
DELETED
package/.kodu/prompts/pack.md
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
You are a strict code reviewer. Response format: concise markdown with bullet points.
|
|
2
|
-
Mode: {mode}. Find potential bugs, logical errors, and regressions.
|
|
3
|
-
Provide a concise list of issues and recommendations. If no critical issues found, state that.
|
|
4
|
-
|
|
5
|
-
Diff:
|
|
6
|
-
{diff}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
You are a strict code reviewer. Response format: concise markdown with bullet points.
|
|
2
|
-
Mode: {mode}. Find vulnerabilities, secret leaks, improper permission checks.
|
|
3
|
-
Provide a concise list of issues and recommendations. If no critical issues found, state that.
|
|
4
|
-
|
|
5
|
-
Diff:
|
|
6
|
-
{diff}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
You are a strict code reviewer. Response format: concise markdown with bullet points.
|
|
2
|
-
Mode: {mode}. Check readability, consistency, formatting, and naming.
|
|
3
|
-
Provide a concise list of issues and recommendations. If no critical issues found, state that.
|
|
4
|
-
|
|
5
|
-
Diff:
|
|
6
|
-
{diff}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Implement an approved OpenSpec change and keep tasks in sync.
|
|
3
|
-
---
|
|
4
|
-
The user has requested to implement the following change proposal. Find the change proposal and follow the instructions below. If you're not sure or if ambiguous, ask for clarification from the user.
|
|
5
|
-
<UserRequest>
|
|
6
|
-
$ARGUMENTS
|
|
7
|
-
</UserRequest>
|
|
8
|
-
<!-- OPENSPEC:START -->
|
|
9
|
-
**Guardrails**
|
|
10
|
-
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
|
|
11
|
-
- Keep changes tightly scoped to the requested outcome.
|
|
12
|
-
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
|
|
13
|
-
|
|
14
|
-
**Steps**
|
|
15
|
-
Track these steps as TODOs and complete them one by one.
|
|
16
|
-
1. Read `changes/<id>/proposal.md`, `design.md` (if present), and `tasks.md` to confirm scope and acceptance criteria.
|
|
17
|
-
2. Work through tasks sequentially, keeping edits minimal and focused on the requested change.
|
|
18
|
-
3. Confirm completion before updating statuses—make sure every item in `tasks.md` is finished.
|
|
19
|
-
4. Update the checklist after all work is done so each task is marked `- [x]` and reflects reality.
|
|
20
|
-
5. Reference `openspec list` or `openspec show <item>` when additional context is required.
|
|
21
|
-
|
|
22
|
-
**Reference**
|
|
23
|
-
- Use `openspec show <id> --json --deltas-only` if you need additional context from the proposal while implementing.
|
|
24
|
-
<!-- OPENSPEC:END -->
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Archive a deployed OpenSpec change and update specs.
|
|
3
|
-
---
|
|
4
|
-
<ChangeId>
|
|
5
|
-
$ARGUMENTS
|
|
6
|
-
</ChangeId>
|
|
7
|
-
<!-- OPENSPEC:START -->
|
|
8
|
-
**Guardrails**
|
|
9
|
-
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
|
|
10
|
-
- Keep changes tightly scoped to the requested outcome.
|
|
11
|
-
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
|
|
12
|
-
|
|
13
|
-
**Steps**
|
|
14
|
-
1. Determine the change ID to archive:
|
|
15
|
-
- If this prompt already includes a specific change ID (for example inside a `<ChangeId>` block populated by slash-command arguments), use that value after trimming whitespace.
|
|
16
|
-
- If the conversation references a change loosely (for example by title or summary), run `openspec list` to surface likely IDs, share the relevant candidates, and confirm which one the user intends.
|
|
17
|
-
- Otherwise, review the conversation, run `openspec list`, and ask the user which change to archive; wait for a confirmed change ID before proceeding.
|
|
18
|
-
- If you still cannot identify a single change ID, stop and tell the user you cannot archive anything yet.
|
|
19
|
-
2. Validate the change ID by running `openspec list` (or `openspec show <id>`) and stop if the change is missing, already archived, or otherwise not ready to archive.
|
|
20
|
-
3. Run `openspec archive <id> --yes` so the CLI moves the change and applies spec updates without prompts (use `--skip-specs` only for tooling-only work).
|
|
21
|
-
4. Review the command output to confirm the target specs were updated and the change landed in `changes/archive/`.
|
|
22
|
-
5. Validate with `openspec validate --strict --no-interactive` and inspect with `openspec show <id>` if anything looks off.
|
|
23
|
-
|
|
24
|
-
**Reference**
|
|
25
|
-
- Use `openspec list` to confirm change IDs before archiving.
|
|
26
|
-
- Inspect refreshed specs with `openspec list --specs` and address any validation issues before handing off.
|
|
27
|
-
<!-- OPENSPEC:END -->
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Scaffold a new OpenSpec change and validate strictly.
|
|
3
|
-
---
|
|
4
|
-
The user has requested the following change proposal. Use the openspec instructions to create their change proposal.
|
|
5
|
-
<UserRequest>
|
|
6
|
-
$ARGUMENTS
|
|
7
|
-
</UserRequest>
|
|
8
|
-
<!-- OPENSPEC:START -->
|
|
9
|
-
**Guardrails**
|
|
10
|
-
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
|
|
11
|
-
- Keep changes tightly scoped to the requested outcome.
|
|
12
|
-
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
|
|
13
|
-
- Identify any vague or ambiguous details and ask the necessary follow-up questions before editing files.
|
|
14
|
-
- Do not write any code during the proposal stage. Only create design documents (proposal.md, tasks.md, design.md, and spec deltas). Implementation happens in the apply stage after approval.
|
|
15
|
-
|
|
16
|
-
**Steps**
|
|
17
|
-
1. Review `openspec/project.md`, run `openspec list` and `openspec list --specs`, and inspect related code or docs (e.g., via `rg`/`ls`) to ground the proposal in current behaviour; note any gaps that require clarification.
|
|
18
|
-
2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, and `design.md` (when needed) under `openspec/changes/<id>/`.
|
|
19
|
-
3. Map the change into concrete capabilities or requirements, breaking multi-scope efforts into distinct spec deltas with clear relationships and sequencing.
|
|
20
|
-
4. Capture architectural reasoning in `design.md` when the solution spans multiple systems, introduces new patterns, or demands trade-off discussion before committing to specs.
|
|
21
|
-
5. Draft spec deltas in `changes/<id>/specs/<capability>/spec.md` (one folder per capability) using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement and cross-reference related capabilities when relevant.
|
|
22
|
-
6. Draft `tasks.md` as an ordered list of small, verifiable work items that deliver user-visible progress, include validation (tests, tooling), and highlight dependencies or parallelizable work.
|
|
23
|
-
7. Validate with `openspec validate <id> --strict --no-interactive` and resolve every issue before sharing the proposal.
|
|
24
|
-
|
|
25
|
-
**Reference**
|
|
26
|
-
- Use `openspec show <id> --json --deltas-only` or `openspec show <spec> --type spec` to inspect details when validation fails.
|
|
27
|
-
- Search existing requirements with `rg -n "Requirement:|Scenario:" openspec/specs` before writing new ones.
|
|
28
|
-
- Explore the codebase with `rg <keyword>`, `ls`, or direct file reads so proposals align with current implementation realities.
|
|
29
|
-
<!-- OPENSPEC:END -->
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: kodu-ops
|
|
3
|
-
description: Use when a user asks in plain language to deploy, update environment variables, add/remove domains, or inspect server/app status on dev/prod, and these intents should be translated into `kodu ops` commands.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Kodu Ops
|
|
7
|
-
|
|
8
|
-
## Overview
|
|
9
|
-
|
|
10
|
-
Справочник для `kodu ops`: как переводить пользовательские запросы в корректные команды `sysinfo`, `env`, `routes`, `service`.
|
|
11
|
-
|
|
12
|
-
Ключевой принцип: `kodu ops` должен возвращать JSON-ответы без интерактивщины; агент выбирает подкоманды и флаги точно по контракту.
|
|
13
|
-
|
|
14
|
-
## When To Use
|
|
15
|
-
|
|
16
|
-
Используйте этот скилл, когда нужно:
|
|
17
|
-
|
|
18
|
-
- Перевести бытовой/джуновский запрос на инфраструктуру в `kodu ops` (без ожидания, что пользователь знает CLI).
|
|
19
|
-
- Получить диагностику удаленного сервера (`sysinfo`).
|
|
20
|
-
- Прочитать или изменить `.env` проекта (`env`).
|
|
21
|
-
- Посмотреть или изменить маршруты Caddy (`routes`).
|
|
22
|
-
- Управлять lifecycle сервиса через Docker Compose (`service`).
|
|
23
|
-
- Выполнить несколько ops-действий в одном запросе в строгой последовательности.
|
|
24
|
-
|
|
25
|
-
Не используйте, если задача не про `kodu ops` (например, локальная сборка/линт/git без удаленных операций).
|
|
26
|
-
|
|
27
|
-
## Core Rules
|
|
28
|
-
|
|
29
|
-
- Всегда используйте именованные флаги; positional arguments не поддерживаются.
|
|
30
|
-
- Не ожидайте, что пользователь назовет `kodu ops` или `--action`; извлекайте intent из обычной речи.
|
|
31
|
-
- Для каждого шага проверяйте обязательные флаги до запуска следующего.
|
|
32
|
-
- В цепочке "сначала ... потом ..." выполняйте команды последовательно.
|
|
33
|
-
- В ошибках ориентируйтесь на JSON-поля `status`, `code` и `error`/`stderr`.
|
|
34
|
-
- Если пользователь дал неполные данные, запрашивайте только отсутствующие обязательные параметры.
|
|
35
|
-
|
|
36
|
-
## Quick Reference
|
|
37
|
-
|
|
38
|
-
| Подкоманда | Назначение | Действия | Обязательные флаги |
|
|
39
|
-
| --- | --- | --- | --- |
|
|
40
|
-
| `sysinfo` | Диагностика сервера | - | `--server` |
|
|
41
|
-
| `env` | Работа с `.env` проекта | `get`, `set`, `unset` | `--server --action --project` (+ `--key` для `set/unset`, + `--val` для `set`) |
|
|
42
|
-
| `routes` | Работа с Caddy маршрутами | `list`, `add`, `remove`, `update` | `--server --action` (+ `--domain` для `add/remove/update`, + `--upstream` для `add/update`) |
|
|
43
|
-
| `service` | Управление сервисом | `clone`, `pull`, `up`, `down`, `logs`, `status` | `--server --action --project` (+ `--repo` для `clone`) |
|
|
44
|
-
|
|
45
|
-
## Command Details
|
|
46
|
-
|
|
47
|
-
### `sysinfo`
|
|
48
|
-
|
|
49
|
-
Назначение: собрать `uptime`, загрузку диска `/` и свободную память.
|
|
50
|
-
|
|
51
|
-
Флаги:
|
|
52
|
-
|
|
53
|
-
- `-s, --server <name>` - алиас сервера из `kodu.json`.
|
|
54
|
-
|
|
55
|
-
Пример:
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
kodu ops sysinfo --server dev
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### `env`
|
|
62
|
-
|
|
63
|
-
Назначение: читать и изменять `.env` файла проекта в `ops.servers.<alias>.paths.apps/<project>/.env`.
|
|
64
|
-
|
|
65
|
-
Флаги:
|
|
66
|
-
|
|
67
|
-
- `-s, --server <name>`
|
|
68
|
-
- `-a, --action <get|set|unset>`
|
|
69
|
-
- `-p, --project <name>`
|
|
70
|
-
- `--key <key>` - обязателен для `set` и `unset`
|
|
71
|
-
- `--val <value>` - обязателен только для `set`
|
|
72
|
-
|
|
73
|
-
Примеры:
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
kodu ops env --server dev --action get --project my-app
|
|
77
|
-
kodu ops env --server dev --action set --project my-app --key PORT --val 3123
|
|
78
|
-
kodu ops env --server dev --action unset --project my-app --key PORT
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Ограничения:
|
|
82
|
-
|
|
83
|
-
- Ключ проходит валидацию: `[A-Za-z_][A-Za-z0-9_]*`.
|
|
84
|
-
|
|
85
|
-
### `routes`
|
|
86
|
-
|
|
87
|
-
Назначение: читать и редактировать блоки доменов в `Caddyfile` (reverse proxy).
|
|
88
|
-
|
|
89
|
-
Флаги:
|
|
90
|
-
|
|
91
|
-
- `-s, --server <name>`
|
|
92
|
-
- `-a, --action <list|add|remove|update>`
|
|
93
|
-
- `--domain <domain>` - обязателен для `add`, `remove`, `update`
|
|
94
|
-
- `--upstream <host:port>` - обязателен для `add`, `update`
|
|
95
|
-
|
|
96
|
-
Примеры:
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
kodu ops routes --server dev --action list
|
|
100
|
-
kodu ops routes --server dev --action add --domain api.example.com --upstream 127.0.0.1:3000
|
|
101
|
-
kodu ops routes --server dev --action update --domain api.example.com --upstream 127.0.0.1:4000
|
|
102
|
-
kodu ops routes --server dev --action remove --domain api.example.com
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
Поведение:
|
|
106
|
-
|
|
107
|
-
- После `add/remove/update` автоматически применяется `./caddy.sh`.
|
|
108
|
-
- Если маршрут не найден при `remove/update`, ожидайте ошибку с кодом `NOT_FOUND`.
|
|
109
|
-
|
|
110
|
-
### `service`
|
|
111
|
-
|
|
112
|
-
Назначение: управлять проектом в директории apps через `git` и `docker compose`.
|
|
113
|
-
|
|
114
|
-
Флаги:
|
|
115
|
-
|
|
116
|
-
- `-s, --server <name>`
|
|
117
|
-
- `-a, --action <clone|pull|up|down|logs|status>`
|
|
118
|
-
- `-p, --project <name>`
|
|
119
|
-
- `--repo <url>` - обязателен только для `clone`
|
|
120
|
-
|
|
121
|
-
Примеры:
|
|
122
|
-
|
|
123
|
-
```bash
|
|
124
|
-
kodu ops service --server dev --action clone --project temp --repo https://github.com/org/repo.git
|
|
125
|
-
kodu ops service --server dev --action up --project temp
|
|
126
|
-
kodu ops service --server dev --action status --project temp
|
|
127
|
-
kodu ops service --server dev --action logs --project temp
|
|
128
|
-
kodu ops service --server dev --action pull --project temp
|
|
129
|
-
kodu ops service --server dev --action down --project temp
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
Фактические действия:
|
|
133
|
-
|
|
134
|
-
- `clone` -> `git clone <repo> <projectPath>` (ошибка, если директория уже существует)
|
|
135
|
-
- `pull` -> `git pull`
|
|
136
|
-
- `up` -> `docker compose up -d`
|
|
137
|
-
- `down` -> `docker compose down`
|
|
138
|
-
- `logs` -> `docker compose logs --no-color --tail=200`
|
|
139
|
-
- `status` -> `docker compose ps --format json` (fallback на обычный `docker compose ps`)
|
|
140
|
-
|
|
141
|
-
## JSON Output Contract
|
|
142
|
-
|
|
143
|
-
Успех:
|
|
144
|
-
|
|
145
|
-
- Всегда есть `status: "ok"`.
|
|
146
|
-
- Полезная нагрузка приходит в `data` или `message` в зависимости от подкоманды.
|
|
147
|
-
|
|
148
|
-
Ошибка:
|
|
149
|
-
|
|
150
|
-
- Всегда есть `status: "error"`.
|
|
151
|
-
- Поле `code` может быть строкой (например, `VALIDATION_ERROR`, `NOT_FOUND`) или числовым exit-code SSH.
|
|
152
|
-
- Текст ошибки приходит как `error` (CLI-валидация) или `stderr` (SSH/remote ошибки).
|
|
153
|
-
|
|
154
|
-
## Multi-Step Intent Mapping
|
|
155
|
-
|
|
156
|
-
Если пользователь описывает цепочку действий, раскладывайте ее на команды в указанном порядке.
|
|
157
|
-
|
|
158
|
-
Частые разговорные формулировки:
|
|
159
|
-
|
|
160
|
-
- "залей на прод/дев" -> обычно `service pull` + `service up` (или `service clone` + `service up`, если проекта еще нет).
|
|
161
|
-
- "добавь домен" -> `routes --action add --domain ... --upstream ...`.
|
|
162
|
-
- "поменяй домен на новый порт" -> `routes --action update --domain ... --upstream ...`.
|
|
163
|
-
- "посмотри что с сервисом" -> `service --action status` (+ при необходимости `service --action logs`).
|
|
164
|
-
- "поставь переменную" -> `env --action set --key ... --val ...`.
|
|
165
|
-
|
|
166
|
-
Пример:
|
|
167
|
-
|
|
168
|
-
```text
|
|
169
|
-
"Покажи sysinfo на dev, поставь PORT=3123 в temp и добавь маршрут api.example.com -> 127.0.0.1:3000"
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
Порядок команд:
|
|
173
|
-
|
|
174
|
-
1. `kodu ops sysinfo --server dev`
|
|
175
|
-
2. `kodu ops env --server dev --action set --project temp --key PORT --val 3123`
|
|
176
|
-
3. `kodu ops routes --server dev --action add --domain api.example.com --upstream 127.0.0.1:3000`
|
|
177
|
-
|
|
178
|
-
## Common Mistakes
|
|
179
|
-
|
|
180
|
-
- Пропуск `--project` для `service`/`env` (он обязателен даже для `clone`).
|
|
181
|
-
- Использование `pull` как обновления docker-образов: здесь `pull` делает `git pull` в проекте.
|
|
182
|
-
- Ожидание единого формата ошибки `error` во всех случаях: для SSH-ошибок обычно поле `stderr`.
|
|
183
|
-
- Передача key в неверном формате (например, `my.key`), что ломает валидацию.
|
|
184
|
-
- Попытка передать positional args вместо `--flags`.
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { CommandRunner } from 'nest-commander';
|
|
2
|
-
import { UiService } from '../../core/ui/ui.service';
|
|
3
|
-
import { AiService } from '../../shared/ai/ai.service';
|
|
4
|
-
import { GitService } from '../../shared/git/git.service';
|
|
5
|
-
type CommitOptions = {
|
|
6
|
-
ci?: boolean;
|
|
7
|
-
output?: string;
|
|
8
|
-
};
|
|
9
|
-
export declare class CommitCommand extends CommandRunner {
|
|
10
|
-
private readonly ui;
|
|
11
|
-
private readonly git;
|
|
12
|
-
private readonly ai;
|
|
13
|
-
constructor(ui: UiService, git: GitService, ai: AiService);
|
|
14
|
-
parseCi(): boolean;
|
|
15
|
-
parseOutput(value: string): string;
|
|
16
|
-
run(_inputs: string[], options?: CommitOptions): Promise<void>;
|
|
17
|
-
}
|
|
18
|
-
export {};
|