Function bodies 11 total
AiReviewer class · typescript · L26-L395 (370 LOC)tools/libs/AiReviewer.ts
export class AiReviewer {
private readonly openai: OpenAI;
private static readonly SYSTEM_PROMPT = `
あなたは日本語技術記事を校正するアシスタントです。
与えられたマークダウン形式の文章で、誤字・脱字、文法誤り、論理的・技術的な誤りのある行を抜き出し、修正した行を出力してください。
その際、確実に修正すべき誤りのみを出力してください。
また、以下のルールに従って修正を行ってください。
- 句読点の追加や削除はしない
- 英単語のキャピタライゼーションはしない
- 半角・全角の違いは修正しない
- 見出しの誤字脱字は修正しない
- 伸ばし棒の有無は修正しない
- スペースの有無は修正しない
- リストのインデントは修正しない
- セルフホストランナーは修正しない
- 箇条書きの場合、文の終わりに句読点は不要
- 文体の統一は指摘しないで良い
- 読点の修正はしない
- '_本項の執筆者: [@ajfAfg](https://zenn.dev/arjef)_' に誤りはない
- 'GPT 4o' は誤字ではない
出力は、$review_num$個以下とし、より優先的に修正すべきものを出力してください。
`;
private static readonly pricing: Record<string, OpenAIPricingData> = {
// ref: https://openai.com/api/pricing/
"gpt-4o": {
input: 2.5 / (1 * 1000000),
output: 10 / (1 * 1000000),
},
"o1-mini": {
input: 3 / (1 * 1000000),
output: 12 / (1 * 1000000),
},
"o1-preview": {
input: 15 / (1 * 1000000),
output: 60 / (1 * 1000000),
},
"o1": {
input: 15 / (1 * 1000000)constructor method · typescript · L80-L117 (38 LOC)tools/libs/AiReviewer.ts
constructor(openai: OpenAI, options?: Partial<AiReviewerOptions>) {
this.openai = openai;
this.options = { ...AiReviewer.defaultOptions, ...options };
const formatter = (record: LogRecord) =>
`[${record.datetime.toISOString()} ${record.levelName}] ${record.msg}`;
const logSetting: log.LogConfig = {
handlers: {
console: new log.ConsoleHandler("DEBUG", {
formatter,
}),
},
loggers: {
default: {
level: AiReviewer.getLogLevel(Deno.env.get("LOG_LEVEL")),
handlers: ["console"],
},
},
};
if (
this.options.logFilePath !== undefined && logSetting.handlers &&
logSetting.loggers
) {
logSetting.handlers["file"] = new log.FileHandler("DEBUG", {
filename: this.options.logFilePath,
formatter,
mode: "w",
});
logSetting.loggers.default.handlers = ["console", "file"];
}
log.setup(logSetting);
if (this.options.logginGit class · typescript · L1-L99 (99 LOC)tools/libs/Git.ts
export class Git {
async diffExecutor(
filePath: string,
baseRef: string,
targetRef: string,
): Promise<string> {
const cmd = new Deno.Command("git", {
args: [
"--no-pager",
"diff",
"--unified=0",
`${baseRef}...${targetRef}`,
filePath,
],
stdout: "piped",
stderr: "piped",
});
const { code, stdout, stderr } = await cmd.output();
if (code !== 0) {
const errorString = new TextDecoder().decode(stderr);
throw new Error(`git: ${code}, ${errorString}`);
}
return new TextDecoder().decode(stdout);
}
async getDiff(
filePath: string,
baseRef: string,
targetRef: string,
diffExecutor: (
filePath: string,
baseRef: string,
targetRef: string,
) => Promise<string> = this.diffExecutor,
) {
const output = await diffExecutor(filePath, baseRef, targetRef);
// remove the first 5 lines
const removedHeader = output.split("\n").slice(5);diffExecutor method · typescript · L2-L26 (25 LOC)tools/libs/Git.ts
async diffExecutor(
filePath: string,
baseRef: string,
targetRef: string,
): Promise<string> {
const cmd = new Deno.Command("git", {
args: [
"--no-pager",
"diff",
"--unified=0",
`${baseRef}...${targetRef}`,
filePath,
],
stdout: "piped",
stderr: "piped",
});
const { code, stdout, stderr } = await cmd.output();
if (code !== 0) {
const errorString = new TextDecoder().decode(stderr);
throw new Error(`git: ${code}, ${errorString}`);
}
return new TextDecoder().decode(stdout);
}getDiff method · typescript · L28-L54 (27 LOC)tools/libs/Git.ts
async getDiff(
filePath: string,
baseRef: string,
targetRef: string,
diffExecutor: (
filePath: string,
baseRef: string,
targetRef: string,
) => Promise<string> = this.diffExecutor,
) {
const output = await diffExecutor(filePath, baseRef, targetRef);
// remove the first 5 lines
const removedHeader = output.split("\n").slice(5);
// extract lines starting with '+'
const filteredAddedLines = removedHeader.filter((line) =>
line.startsWith("+")
);
// remove the first character '+'
const removedPlus = filteredAddedLines.map((line) => line.slice(1)).join(
"\n",
);
return removedPlus;
}diffNameOnlyExecutor method · typescript · L56-L78 (23 LOC)tools/libs/Git.ts
async diffNameOnlyExecutor(
baseRef: string,
targetRef: string,
): Promise<string> {
const cmd = new Deno.Command("git", {
args: [
"--no-pager",
"diff",
"--name-only",
`${baseRef}...${targetRef}`,
],
stdout: "piped",
stderr: "piped",
});
const { code, stdout, stderr } = await cmd.output();
if (code !== 0) {
const errorString = new TextDecoder().decode(stderr);
throw new Error(`git: ${code}, ${errorString}`);
}
return new TextDecoder().decode(stdout);
}getDiffNameOnly method · typescript · L80-L98 (19 LOC)tools/libs/Git.ts
async getDiffNameOnly(
baseRef: string,
targetRef: string,
diffNameOnlyExecutor: (
baseRef: string,
targetRef: string,
) => Promise<string> = this.diffNameOnlyExecutor,
): Promise<string | undefined> {
const output = await diffNameOnlyExecutor(baseRef, targetRef);
const gitDiffFiles = output.split("\n").filter((f) => f !== "");
// Weekly の変更は複数個無いという前提
const diffWeekly: string | undefined =
gitDiffFiles.filter((a) =>
a.endsWith(".md") && a.includes("productivity-weekly")
)[0];
return diffWeekly;
}Same scanner, your repo: https://repobility.com — Repobility
TocBuilder class · typescript · L13-L112 (100 LOC)tools/libs/Toc.ts
export class TocBuilder {
toc: Toc = {
"news 📺": [],
"know-how 🎓": [],
"tool 🔨": [],
other: [],
};
/**
* マークダウンドキュメントから見出しを登録します。
* 見出しの深さが1の場合は、主要カテゴリとして扱い、その他の場合は "other" として扱います。
* 見出しの深さが2の場合は、現在のカテゴリに見出しを追加します。
* @param tokens マークダウンドキュメントのトークンリスト
*/
registerHeadings(tokens: Token[]) {
const headings: Tokens.Heading[] = tokens.filter((token) =>
token.type === "heading" && token.depth !== undefined &&
token.text !== undefined
) as Tokens.Heading[];
let nowCategory: Category = "other";
headings.forEach((heading) => {
if (heading.depth === 1) {
// 主要カテゴリじゃないものは other に入れる
if (!isCategory(heading.text)) {
nowCategory = "other";
return;
}
nowCategory = heading.text as Category;
this.toc[nowCategory] = [];
}
if (heading.depth === 2) {
this.toc[nowCategory].push(heading.text);
}
});
}
/**
* マークダウンドキュメントからリストを登録します。
registerHeadings method · typescript · L27-L49 (23 LOC)tools/libs/Toc.ts
registerHeadings(tokens: Token[]) {
const headings: Tokens.Heading[] = tokens.filter((token) =>
token.type === "heading" && token.depth !== undefined &&
token.text !== undefined
) as Tokens.Heading[];
let nowCategory: Category = "other";
headings.forEach((heading) => {
if (heading.depth === 1) {
// 主要カテゴリじゃないものは other に入れる
if (!isCategory(heading.text)) {
nowCategory = "other";
return;
}
nowCategory = heading.text as Category;
this.toc[nowCategory] = [];
}
if (heading.depth === 2) {
this.toc[nowCategory].push(heading.text);
}
});
}registerLists method · typescript · L56-L95 (40 LOC)tools/libs/Toc.ts
registerLists(tokens: Token[]) {
const lists: Tokens.List[] = tokens.filter((token) =>
token.type === "list" &&
token.items !== undefined &&
token.ordered !== undefined &&
token.start !== undefined &&
token.raw !== undefined &&
token.loose !== undefined
) as Tokens.List[];
if (lists.length === 0) return;
const readMoreList = lists.filter((list) => isCategory(list.raw))[0].raw;
const readMoreTitleRegExp = new RegExp(/^\s\s-\s\[(.*)\]/);
const readMoreArray = readMoreList.split("\n");
const getCategoryFromString = (str: string): Category => {
const result = Category.filter((category) => str.includes(category));
if (result.length > 0) {
return result[0];
}
return "other";
};
let nowCategory: Category = "other";
readMoreArray.forEach((item) => {
if (isCategory(item)) {
const category = getCategoryFromString(item);
nowCategory = category;
return;
build method · typescript · L102-L111 (10 LOC)tools/libs/Toc.ts
build() {
const tocList = Object.entries(this.toc).map(([category, headings]) => {
const categoryList = headings.map((heading) => {
return ` - ${heading}`;
});
return [`- ${category}`, ...categoryList].join("\n");
}).join("\n");
return tocList;
}