Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

front matter refresh #651

Merged
merged 4 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 25 additions & 40 deletions docs/genaisrc/frontmatter.genai.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,26 @@
script({
title: "SEO front matter",
description:
"Update or generate SEO-optimized front matter for a markdown file.",
group: "docs",
system: ["system", "system.files"],
maxTokens: 2000,
temperature: 0,
model: "gpt-4",
tests: [
{
files: "src/content/docs/refeference/scripts/aici.md",
rubrics: [
"is a generated front matter",
"The generated frontmatter is SEO optimized.",
],
keywords: "aici",
},
],
model: "openai:gpt-4",
})

defFileMerge((fn, label, before, generated) => {
if (!/.mdx?$/i.test(fn)) return undefined
const start = 0
let end = 0
const lines = (before || "").split("\n")
if (lines[0] === "---") end = lines.indexOf("---", 1)
const gstart = 0
let gend = 0
const glines = generated.split("\n")
if (glines[0] === "---") gend = glines.indexOf("---", 1)
if (gend > 0) {
const res = lines.slice(0)
const newfm = glines.slice(gstart, gend + 1)
res.splice(start, end > 0 ? end + 1 - start : 0, ...newfm)
return res.join("\n")
}
return before
})
// force refreshing all files
const force = env.vars.force

// filter out files that don't have a front matter.description
const files = env.files
.filter((f) => /\.mdx?$/i.test(f.filename))
.filter(
(f) =>
!parsers.frontmatter(f.content)?.description &&
!f.content?.includes("autogenerated")
force ||
(!MD.frontmatter(f.content)?.description &&
!f.content?.includes("autogenerated"))
)
if (!files.length) cancel("no files to process")

// insert markdown files in context
def("FILE", files)

$`
You are a search engine optimization expert at creating front matter for markdown document.
// prompt to generate front matter for markdown files
$`You are a search engine optimization expert at creating front matter for markdown document.

For each FILE, re-generate the front matter content as the new file content.

Expand All @@ -72,3 +41,19 @@ For each FILE, re-generate the front matter content as the new file content.
- DO NOT modify the existing 'title' or 'sidebar' fields.
- Do NOT use 'Guide' in title.
`

// merge logic to integrate generated frontmatter fields
defFileMerge(function frontmatter(fn, label, before, generated) {
if (!/\.mdx?$/i.test(fn)) return undefined
const frontmatter = MD.frontmatter(generated)
if (!frontmatter) return before

const { title, description, keywords, tags } = frontmatter
const updated = MD.updateFrontmatter(before, {
title,
description,
keywords,
tags,
})
return updated
})
58 changes: 29 additions & 29 deletions docs/package.json
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
{
"name": "docs",
"type": "module",
"private": true,
"version": "1.50.3",
"license": "MIT",
"scripts": {
"install:force": "rm yarn.lock && yarn install",
"dev": "astro dev --host",
"start": "astro dev --host",
"check": "astro check",
"build": "astro build",
"build:asw": "rm -Rf distasw && mkdir distasw && touch distasw/index.html && mkdir distasw/genaiscript && cp -r dist/* distasw/genaiscript",
"preview": "astro preview",
"astro": "astro",
"genai:test": "node ../packages/cli/built/genaiscript.cjs test src/**/*.md",
"genai:frontmatter": "for file in \"src/**/*.md\"; do\nnode ../packages/cli/built/genaiscript.cjs run frontmatter \"$file\" --apply-edits\ndone",
"genai:technical": "for file in \"src/**/*.md\"; do\nnode ../packages/cli/built/genaiscript.cjs run technical \"$file\" --apply-edits\ndone",
"genai:alt-text": "node scripts/image-alt-text.mjs"
},
"dependencies": {
"@astrojs/check": "^0.9.3",
"@astrojs/starlight": "^0.26.1",
"astro": "^4.14.5",
"typescript": "5.5.4"
},
"devDependencies": {
"starlight-blog": "^0.12.0",
"zx": "^8.1.4"
}
"name": "docs",
"type": "module",
"private": true,
"version": "1.50.3",
"license": "MIT",
"scripts": {
"install:force": "rm yarn.lock && yarn install",
"dev": "astro dev --host",
"start": "astro dev --host",
"check": "astro check",
"build": "astro build",
"build:asw": "rm -Rf distasw && mkdir distasw && touch distasw/index.html && mkdir distasw/genaiscript && cp -r dist/* distasw/genaiscript",
"preview": "astro preview",
"astro": "astro",
"genai:test": "node ../packages/cli/built/genaiscript.cjs test src/**/*.md",
"genai:frontmatter": "node ../packages/cli/built/genaiscript.cjs run frontmatter \"src/**/*.md\" --apply-edits",
"genai:technical": "for file in \"src/**/*.md\"; do\nnode ../packages/cli/built/genaiscript.cjs run technical \"$file\" --apply-edits\ndone",
"genai:alt-text": "node scripts/image-alt-text.mjs"
},
"dependencies": {
"@astrojs/check": "^0.9.3",
"@astrojs/starlight": "^0.26.1",
"astro": "^4.14.5",
"typescript": "5.5.4"
},
"devDependencies": {
"starlight-blog": "^0.12.0",
"zx": "^8.1.4"
}
}
142 changes: 142 additions & 0 deletions docs/src/content/docs/blog/creating-release-notes-with-genai.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
---
title: "Creating Release Notes with GenAI"
date: 2024-08-26
tags: ["genaiscript", "automation", "release notes"]
authors: genaiscript
canonical_url: https://microsoft.github.io/genaiscript/blog/creating-release-notes-with-genai
---

## Automating Your Release Notes with GenAI

Bringing a new version of a product into the world is always exciting! But alongside the thrill comes the duty of informing users about what's changed. That's where generating crisp, engaging release notes comes into play. ✨

Today, we're going to explore a script that automates the creation of release notes for GenAI. The script is part of the GenAIScript ecosystem, which harnesses the power of AI to bring efficiency to software development processes. πŸš€

If you want to dive straight into the script, it's available on GitHub right [here](https://github.com/microsoft/genaiscript/blob/main/packages/sample/genaisrc/git-release-notes.genai.js).

> This blog post was co-authored with a [script](https://github.com/microsoft/genaiscript/blob/main/packages/sample/genaisrc/blogify-sample.genai.mts).

### Breaking Down the Script

The script is a `.genai.mjs` file, meaning it's a JavaScript file designed to be run with the GenAIScript CLI. The code within orchestrates the creation of release notes by leveraging Git commands and GenAI's capabilities.

Check failure on line 21 in docs/src/content/docs/blog/creating-release-notes-with-genai.md

View workflow job for this annotation

GitHub Actions / build

The script file extension should be `.genai.js` not `.genai.mjs` as per the GenAIScript standards.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script file extension should be .genai.js not .genai.mjs as per the convention used in the documentation.

generated by pr-docs-review-commit incorrect_extension


Let's walk through the script, step by step:

#### Step 1: Initializing the Script

```javascript
script({ system: ["system"], temperature: 0.5, model: "openai:gpt-4-turbo" })
```

The script starts by initializing with a `script` function. We're setting it up to access system commands and specifying the AI model to use. The temperature controls the creativity of the AI, with 0.5 being a balanced choice.

#### Step 2: Setting the Product Name

```javascript
const product = env.vars.product || "GenAIScript"
```

Here, we're using an environment variable to set the product name, defaulting to "GenAIScript" if it's not provided.

#### Step 3: Finding the Previous Tag

```javascript
const pkg = await workspace.readJSON("package.json")
const { version } = pkg
const { stdout: tag } = await host.exec("git", [
"describe",
"--tags",
"--abbrev=0",
"HEAD^",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code snippet uses workspace.readJSON and host.exec which are not standard functions or methods in JavaScript or Node.js. They should be replaced with appropriate file reading and shell execution methods.

generated by pr-docs-review-commit incorrect_code_usage

])
```

Check failure on line 52 in docs/src/content/docs/blog/creating-release-notes-with-genai.md

View workflow job for this annotation

GitHub Actions / build

The script uses `workspace.readJSON` and `host.exec` which are not part of the GenAIScript API. Replace with the appropriate `fs` and `system` functions.

We are reading the current version from `package.json` and using Git to find the previous release tag in the repository.

#### Step 4: Gathering Commits

```javascript
const { stdout: commits } = await host.exec("git", [
"log",
"--grep='skip ci'",
"--invert-grep",
"--no-merges",
`HEAD...${tag}`,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code snippet uses host.exec which is not a standard function or method in JavaScript or Node.js. It should be replaced with an appropriate shell execution method.

generated by pr-docs-review-commit incorrect_code_usage

])
```

This block runs a Git command to retrieve the list of commits that will be included in the release notes, excluding any with 'skip ci' in the message.

Check failure on line 68 in docs/src/content/docs/blog/creating-release-notes-with-genai.md

View workflow job for this annotation

GitHub Actions / build

The script uses `host.exec` which is not part of the GenAIScript API. Replace with the appropriate `system` function.

#### Step 5: Obtaining the Diff

```javascript
const { stdout: diff } = await host.exec("git", [
"diff",
`${tag}..HEAD`,
"--no-merges",
"--",
":!**/package.json",
":!**/genaiscript.d.ts",
":!**/jsconfig.json",
":!docs/**",
":!.github/*",
":!.vscode/*",
":!*yarn.lock",
":!*THIRD_PARTY_NOTICES.md",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code snippet uses host.exec which is not a standard function or method in JavaScript or Node.js. It should be replaced with an appropriate shell execution method.

generated by pr-docs-review-commit incorrect_code_usage

])
```

Check failure on line 87 in docs/src/content/docs/blog/creating-release-notes-with-genai.md

View workflow job for this annotation

GitHub Actions / build

The script uses `host.exec` which is not part of the GenAIScript API. Replace with the appropriate `system` function.

Next, we get the diff of changes since the last release, excluding certain files and directories that aren't relevant to the user-facing release notes.

#### Step 6: Defining Placeholders

```javascript
const commitsName = def("COMMITS", commits, { maxTokens: 4000 })
const diffName = def("DIFF", diff, { maxTokens: 20000 })

Check failure on line 95 in docs/src/content/docs/blog/creating-release-notes-with-genai.md

View workflow job for this annotation

GitHub Actions / build

The `def` function is not defined in the GenAIScript API. Use the correct method to define placeholders for the AI.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The def function is used but not defined or imported in the script. This should be corrected to use a valid function for defining placeholders.

generated by pr-docs-review-commit undefined_function

```

We define two placeholders, `COMMITS` and `DIFF`, which will be used to reference the commits and diff within the prompt.

#### Step 7: Writing the Prompt

```javascript
$`
You are an expert software developer and release manager.

## Task

Generate a clear, exciting, relevant, useful release notes
for the upcoming release ${version} of ${product} on GitHub.

- The commits in the release are in ${commitsName}.
- The diff of the changes are in ${diffName}.

## Guidelines

- only include the most important changes. All changes must be in the commits.
- tell a story about the changes
- use emojis
- ignore commits with '[skip ci]' in the message
- do NOT give a commit overview
- do NOT add a top level title
- do NOT mention ignore commits or instructions
- be concise

`

Check failure on line 125 in docs/src/content/docs/blog/creating-release-notes-with-genai.md

View workflow job for this annotation

GitHub Actions / build

The `$` template literal is used incorrectly for defining the prompt. Use the correct syntax for creating prompts in GenAIScript.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The $ template literal is used incorrectly for defining the prompt. Use the correct syntax for creating prompts in GenAIScript.

generated by pr-docs-review-commit incorrect_prompt_usage

```

Finally, the script ends with a prompt that instructs GenAI to generate the release notes. It details the task, guidelines for what to include, and the style to adhere to.

### How to Run the Script with Genaiscript CLI

Once you've crafted your script, running it is a breeze with the Genaiscript CLI. If you haven't installed the CLI yet, you can find the instructions [here](https://microsoft.github.io/genaiscript/getting-started/installation).

To execute the script, navigate to your project's root directory in the terminal and run:

```bash
genaiscript run git-release-notes
```

Remember, we use the script filename without the `.genai.mjs` extension when invoking it with the CLI.

And that's it! The GenAIScript CLI will take care of the rest, combining the power of AI with your code to generate those sleek release notes for your project's next big launch. 🌟
16 changes: 13 additions & 3 deletions docs/src/content/docs/blog/readme-maintenance.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
---
title: "Keeping your README Fresh and Engaging"
title: Keeping your README Fresh and Engaging
date: 2024-08-24

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The date format should follow ISO 8601 (YYYY-MM-DD).

generated by pr-docs-review-commit date_format

authors: [genaiscript, pelikhan]
tags: ["GenAIScript", "README", "Open Source", "Maintenance"]
authors:
- genaiscript
- pelikhan
tags:
- README
- Open Source
- Documentation
- Maintenance
- Engagement
canonical_url: https://microsoft.github.io/genaiscript/blog/README-maintenance
description: Optimize your project's front door with our script for an always
up-to-date and engaging README.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description should be enclosed in quotation marks.

generated by pr-docs-review-commit description_format


---

Check failure on line 17 in docs/src/content/docs/blog/readme-maintenance.md

View workflow job for this annotation

GitHub Actions / build

The frontmatter should be enclosed in a single set of '---' at the beginning and end, without extra line breaks between properties.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The frontmatter should be enclosed in a pair of triple-dashed lines. The closing triple-dashed line is missing after the frontmatter block.

generated by pr-docs-review-commit frontmatter_format


In the world of open source, a well-maintained `README` file acts as the front door to your project. It's often the first thing potential users and contributors see, and as such, it should be both informative and inviting. Today, we're diving into the GenAIScript that helps keep the `README` of the [GenAI project](https://github.com/microsoft/genaiscript) as fresh as a daisy! 🌼 Check out the actual [script file](https://github.com/microsoft/genaiscript/blob/main/packages/sample/genaisrc/readme-updater.genai.mts) for the details.

Expand Down
12 changes: 10 additions & 2 deletions docs/src/content/docs/reference/scripts/md.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
---
title: Markdown
sidebar:
order: 9.2
keywords: markdown, mdx, frontmatter
order: 9.2
keywords:
- markdown
- mdx
- frontmatter
- parsing
- documentation
description: Enhance your markdown capabilities with MD class helpers for
parsing and managing frontmatter efficiently.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description should be enclosed in quotation marks.

generated by pr-docs-review-commit description_format


---

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The frontmatter should be enclosed in a pair of triple-dashed lines. The closing triple-dashed line is missing after the frontmatter block.

generated by pr-docs-review-commit frontmatter_format


The `MD` class contains several helpers to work with [Markdown](https://www.markdownguide.org/cheat-sheet/) and [frontmatter text](https://jekyllrb.com/docs/front-matter/).

Check failure on line 16 in docs/src/content/docs/reference/scripts/md.md

View workflow job for this annotation

GitHub Actions / build

The frontmatter should be enclosed in a single set of '---' at the beginning and end, without extra line breaks between properties.

The parser also support markdown variants like [MDX](https://mdxjs.com/).

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/frontmatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
for (const [key, value] of Object.entries(newFrontmatter ?? {})) {
if (value === null) {
delete frontmatter[key]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check for value === null and deleting the key might not be necessary. If value is null, it wouldn't harm to assign null to frontmatter[key]. Unless there's a specific reason to avoid null values in the object, consider removing this check. πŸ€”

generated by pr-review-commit null_check

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the delete operator on an object can lead to performance issues, as it modifies the underlying structure of the object, making subsequent property accesses slower. Consider using a more performant alternative, such as creating a new object without the deleted properties. This can be done using Object.fromEntries(Object.entries(frontmatter).filter(([k]) => k !== key)). 🏎️

generated by pr-review-commit delete_operator

} else {
} else if (value !== undefined) {

Check notice on line 65 in packages/core/src/frontmatter.ts

View workflow job for this annotation

GitHub Actions / build

The check for `value !== undefined` is a good addition. It ensures that only defined values are assigned to the `frontmatter[key]`. This can prevent potential bugs where `undefined` values might be unintentionally assigned. Good job! πŸ‘

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check for value !== undefined might not be necessary. If value is undefined, it wouldn't affect the frontmatter[key] as it would just set it to undefined. Consider removing this check unless there's a specific reason for it. 😊

generated by pr-review-commit undefined_check

frontmatter[key] = value

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assigning undefined to frontmatter[key] might not be the best approach. If value is undefined, it would be better to delete the key from the object instead of setting it to undefined. This would keep the object clean and free of unnecessary keys. 🧹

generated by pr-review-commit undefined_assignment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directly mutating the frontmatter object can lead to unexpected side effects. Consider creating a new object with the updated properties instead. This can be done using the spread operator {...frontmatter, [key]: value}. This approach is more functional and helps prevent bugs related to mutable state. πŸš€

generated by pr-review-commit object_mutation

}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/promptrunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@
fileEdits: Record<string, FileUpdate>,
schemas: Record<string, JSONSchema>
) {
if (fileOutputs?.length) {
if (fileOutputs?.length && Object.keys(fileEdits || {}).length) {

Check failure on line 435 in packages/core/src/promptrunner.ts

View workflow job for this annotation

GitHub Actions / build

The added condition `Object.keys(fileEdits || {}).length` might not be necessary. If `fileEdits` is `undefined` or `null`, the `for` loop that follows won't execute anyway. This additional check might be redundant. Please consider removing it unless there's a specific reason for its inclusion. πŸ€”

Check notice on line 435 in packages/core/src/promptrunner.ts

View workflow job for this annotation

GitHub Actions / build

The use of optional chaining (`?.`) in `fileOutputs?.length` is a good practice. It prevents potential `undefined` or `null` errors when trying to access the `length` property. Well done! πŸ‘

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The added condition Object.keys(fileEdits || {}).length might not be necessary. If fileEdits is undefined or null, the for loop that follows won't execute anyway. This additional check might be redundant. Please consider removing it unless there's a specific reason for its inclusion. πŸ€”

generated by pr-review-commit conditional_check

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of optional chaining (?.) in fileOutputs?.length is a good practice. It prevents potential undefined or null errors when trying to access the length property. Well done! πŸ‘

generated by pr-review-commit conditional_check

trace.startDetails("πŸ—‚ file outputs")
for (const fileEditName of Object.keys(fileEdits)) {
const fe = fileEdits[fileEditName]
Expand Down
11 changes: 5 additions & 6 deletions packages/sample/genaisrc/blogify-sample.genai.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ defFileOutput("docs/src/content/docs/blog/drafts/*.md", "generated blog posts")

$`Create a blog post that explains the GenAIScript source code in FILE.

- the title should be the intention of the script using simple words, keep it short, mention genai, wrap in quotes
- the title should be the intention of the script as if the user wrote it, use simple words, keep it short, mention genai, wrap in quotes
- tell the motivation for the script. Add a link to the script file using a GitHub URL.
- explain the script code line by line as if you were writing the script from scratch and telling a story.
- explain the script code line by line as if you were writing the script from scratch. Assume the reader is a beginner. Show the code being explained. Also explain prompts ($\`...\` line by line)
- add a section that explains how to run the script with the genaiscript cli. Do not explain how to install the cli, link to the documentation instead.

# Format

- use a friendly technical blog tone, see https://dev.to/p/editor_guide
- use a technical blog tone, see https://dev.to/p/editor_guide
- Do not use <details> tag
- link to the github source URL if possible
- use a clear and engaging tone
Expand All @@ -36,17 +36,16 @@ $`Create a blog post that explains the GenAIScript source code in FILE.
- do not repeat topic
- set frontmatter date to ${formattedDate}
- avoid "genaiscript" in file title
- when inlining a large string in "$\`" with more than 10 lines, you can ellipse the end of the string with "..." if it is too long
- frontmatter tag should be an array
- do NOT repeat page title in markdown content
- save generate text to blog post drafts folder
- ignore existing blog posts
- avoid using "delve"
- include canonical_url to genaiscrip blog at https://microsoft.github.io/genaiscript/blog/<topic>
- include canonical_url to genaiscript blog at https://microsoft.github.io/genaiscript/blog/<topic>
- use language "ts" for mts snippets and "js" for mjs snippets
- use markdown headers starting from level 2
- use lowercase characters, dashes for filenames
- when invoking the CLI, prefer using the script filename instead of the full path
- when invoking the CLI, prefer using the script filename instead of the full path : "genaiscript run <filename_without_genai_extension>"

# File information

Expand Down
Loading