# Project export: Promptli

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 11.0
- Tagline: Make the most out of AI with Promptli, your personal prompt engineer: automatic prompt suggestions as you type.
- Devpost: https://devpost.com/software/promptli
- GitHub: https://github.com/crocodile27/Promptli
- Video: https://www.youtube.com/embed/Z_ySRp0I2Xs?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — Bonnie-w (21 commits), Aldrin Ong (17 commits), crocodile27 (11 commits), RiaTao (1 commits)

## Devpost submission (written by the team)

### Inspiration

As students and programmers we can spend countless iterations and follow ups prompting ChatGPT to give us the answer that we are looking for. Often before we reach a working solution we've already used up our tokens for ChatGPT 4.0. Not only is prompting time consuming, but also token-consuming. Thus, we decided to create something convenient and practical that we can use to help us engineer our prompts for large language models.

### What it does

Promptli is a prompt engineering chrome extension that provides personalized suggestions and improves your prompts as you type them live in the chatbot. By using frameworks from our research into prompt engineering, Promptli analyzes the prompt on the following dimensions: Identify any ambiguities, vague terms, or areas that could benefit from more detail or context. Assess the logical flow and organization of the prompt. Evaluate the word choice, vocabulary precision, and overall readability. Determine whether the prompt is appropriately focused and scoped for its intended purpose. Making prompts more "AI-friendly" Make the most out of AI with Promptli, your personal prompt engineer.

### How we built it

We built a chrome extension using: Javascript for the back-end server, scripting, and content rendering HTML & CSS for a user-friendly UI/UX that seamlessly integrates with the chat interface Gemini API for Promptli’s prompt engineering capabilities and JSON file rendering 100ms processing time for real time prompt improvement Effortless rendering of new prompts Interactive and easy-to-use UI/UX with single click per prompt

### Challenges we ran into

\We began this project without prior experience with creating web extensions. Our first step was looking at resources from the Chrome Developers site. Specifically focused on chrome extension sample code and familiarizing ourselves with their API. Our second challenge was integrating our extension with AI platforms in a seamless fashion that was easy to use and wouldn’t block users’ workflow. This posed greater difficulties than we had initially anticipated as we needed to inject our JS into the website's dynamic layout. After several iterations of designs and troubleshooting our extensions compatibility with the AI site’s code, we were able to create something that works alongside the website’s ever changing layout and is intuitive to use. Lastly, having to work in a time-crunch with little to no sleep was definitely challenging but at the same time thrilling to build something with our friends that we truly would use and love as a product.

### Accomplishments we're proud of

We're proud of building an app that we could use ourselves and provides practical value to any daily user of a chat bot. A lot of the time, the world seems overcrowded with artificial intelligence and machines so we are also are proud of creating a more human friendly experience that allows better communication between machine learning models and the human being.

### What we learned

I think the best learning experience was about ourselves and our capabilities when working with friends for something we were passionate about. This was a really fun experience and we loved hacking our persimmon app: Promptli. Other things we learned: how to code a chrome extension how to create a back end server how to utilize javascript to call API and parse it through how to prompt engineer how to prompt chatbots to output file structure how to sleep under a table

### What's next

We're currently working on integration with a wider range of generative AI services and hope to make the product more accessible with future partnerships. We also hope for a custom prompting model for long term sustainability fine tuned for writing good prompts.

## README (from the GitHub repository)

# Promptli 
Make the most out of AI with Promptli, your personal prompt engineer: automatic prompt suggestions as you type.

## How to get started with promptli
1. Clone our repository
  ```git clone git@github.com:crocodile27/Promptli.git```

2. Head over to https://aistudio.google.com/app/apikey and follow the steps to get your API key

3. Put it in express's server.js

4. Go to chrome://extensions/ on chrome browser and click load unpacked. Then click the userScripts folder and click ok.

5. Open chatgpt.com and start using our chrome extension.

## Inspiration
As students and programmers we can spend countless iterations and follow ups prompting ChatGPT to give us the answer that we are looking for. Often before we reach a working solution we've already used up our tokens for ChatGPT 4.0. Not only is prompting time consuming, but also token-consuming. Thus, we decided to create something convenient and practical that we can use to help us engineer our prompts for large language models.

## What it does
Promptli is a prompt engineering chrome extension that provides personalized suggestions and improves your prompts as you type them live in the chatbot. By using frameworks from our research into prompt engineering, Promptli analyzes the prompt on the following dimensions:

1. Identify any ambiguities, vague terms, or areas that could benefit from more detail or context.
2. Assess the logical flow and organization of the prompt. 
3. Evaluate the word choice, vocabulary precision, and overall readability.
4. Determine whether the prompt is appropriately focused and scoped for its intended purpose.
5. Making prompts more "AI-friendly"

Make the most out of AI with Promptli, your personal prompt engineer.

## How we built it
We built a chrome extension using: 
- Javascript for the back-end server, scripting, and content rendering
- HTML & CSS for a user-friendly UI/UX that seamlessly integrates with the chat interface
- Gemini API for Promptli’s prompt engineering capabilities and JSON file rendering
- 100ms processing time for real time prompt improvement
- Effortless rendering of new prompts
- Interactive and easy-to-use UI/UX with single click per prompt

## Challenges we ran into

\We began this project without prior experience with creating web extensions. Our first step was looking at resources from the Chrome Developers site. Specifically focused on chrome extension sample code and familiarizing ourselves with their API.

Our second challenge was integrating our extension with AI platforms in a seamless fashion that was easy to use and wouldn’t block users’ workflow. This posed greater difficulties than we had initially anticipated as we needed to inject our JS into the website's dynamic layout. After several iterations of designs and troubleshooting our extensions compatibility with the AI site’s code, we were able to create something that works alongside the website’s ever changing layout and is intuitive to use.

Lastly, having to work in a time-crunch with little to no sleep was definitely challenging but at the same time thrilling to build something with our friends that we truly would use and love as a product.

## Accomplishments that we're proud of
We're proud of building an app that we could use ourselves and provides practical value to any daily user of a chat bot. A lot of the time, the world seems overcrowded with artificial intelligence and machines so we are also are proud of creating a more human friendly experience that allows better communication between machine learning models and the human being. 

## What we learned
I think the best learning experience was about ourselves and our capabilities when working with friends for something we were passionate about. This was a really fun experience and we loved hacking our persimmon app: Promptli.

Other things we learned:
- how to code a chrome extension
- how to create a back end server
- how to utilize javascript to call API and parse it through
- how to prompt engineer
- how to prompt chatbots to output file structure
- how to sleep under a table

## What's next for Promptli 
We're currently working on integration with a wider range of generative AI services and hope to make the product more accessible with future partnerships. We also hope for a custom prompting model for long term sustainability fine tuned for writing good prompts.



## Detected evidence (automated analysis)

Indexed codebase: 18 recognized source files, 52 KB.
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- Google Gemini (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (23 of 23)

```
.DS_Store
express/package.json
express/server.js
README.md
reference.mv3-content-scripts/content-script.js
reference.mv3-content-scripts/manifest.json
reference.mv3-content-scripts/popup.css
reference.mv3-content-scripts/popup.html
reference.mv3-content-scripts/popup.js
reference.mv3-content-scripts/README.md
scripting/content-script.js
scripting/index.css
scripting/index.html
scripting/index.js
scripting/manifest.json
scripting/README.md
scripting/sw.js
userScripts/background.js
userScripts/buttons.html
userScripts/content.js
userScripts/manifest.json
userScripts/README.md
userScripts/styles.css
```

### Dependencies

- express/package.json: @google/generative-ai@^0.21.0, cors@^2.8.5, dotenv@^16.4.5, express@^4.21.1

### Recent commits (newest first)

- Update README.md
- Update README.md
- incorporate icon change
- remove animations to fix the prompt render
- fixed icon logo for extension
- add close popup on enter or submit prompt
- resolve merge
- loading animation update
- Merge branch 'main' of github.com:crocodile27/Promptli
- Button changed
- reduce typingInterval
- fix hover shade for expanded button
- Add button press and expand
- integrate backend and frontend
- fixed merge conflict
- generates jsons now
- Add perplexity
- fix merge conflict
- changes to manifest.json for persimmon image
- upPersimmon image

## Key source files (fetched from GitHub, selected and truncated for size)

### express/package.json

```
{
  "name": "express",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "@google/generative-ai": "^0.21.0",
    "cors": "^2.8.5",
    "dotenv": "^16.4.5",
    "express": "^4.21.1"
  }
}

```

### express/server.js

```javascript
// server.js
const express = require("express");
const cors = require("cors");
const { GoogleGenerativeAI } = require("@google/generative-ai");
require("dotenv").config();  // Load environment variables

const app = express();
const PORT = 3000;

// Use CORS to allow requests from your front-end
app.use(cors());
app.use(express.json());

// Initialize GoogleGenerativeAI with API key from .env
const apiKey = process.env.GOOGLE_API_KEY;
if (!apiKey) {
  console.error("API Key is missing. Make sure you have a .env file with GOOGLE_API_KEY.");
  process.exit(1); // Stop server if API key is not provided
}

const genAI = new GoogleGenerativeAI(apiKey);

const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash", 
  generationConfig: {
  responseMimeType: "application/json",
},});





app.post("/generate", async (req, res) => {
  const { promptText } = req.body;
  console.log(req.body)
  if (!promptText) {
    return res.status(400).json({ error: "promptText is required" });
  }

  // New detailed prompt
  const prompt = `prompt = """Please analyze the following AI prompt: Prompt: ${promptText}
and provide suggestions for improvement across multiple dimensions and only return a .json file with 3 different objects of this format starting with “{“ and ending with “}” that each contain an improved prompt and a summary of the prompt in less than 15 words that highlights the difference in the prompts. The summary does not need to be grammatically correct.

Specifically, please address the following dimensions in your analysis:

1. Clarity and Specificity: 
- Identify any ambiguities, vague terms, or areas that could benefit from more detail or context.
- Suggest ways to make the prompt clearer, more specific, and easier for an AI to understand and respond to effectively.

2. Structure and Organization:
- Assess the logical flow and organization of the prompt. 
- Recommend ways to restructure the prompt for better coherence and effectiveness, such as using a problem-solution format, step-by-step instructions, or a series of focused questions.

3. Language and Tone:
- Evaluate the language used in the prompt and suggest improvements in word choice, vocabulary precision, and overall readability.

4. Focus and Scope:
- Determine whether the prompt is appropriately focused and scoped for its intended purpose.
- Suggest ways to narrow or expand the focus as needed, and recommend any additional constraints or guidelines that could help elicit more targeted and relevant responses.

5. AI Optimization:
- Provide recommendations for making the prompt more 'AI-friendly', considering factors such as leveraging the AI's strengths, avoiding common pitfalls, and encouraging creative and diverse responses.
- Suggest any AI-specific optimizations or best practices that could enhance the prompt's effectiveness.

Use this JSON schema:

Prompts = {‘prompt’: str, ‘summary’: str}
Return: list[Prompt]"""
`;

  try {
    const result = await model.generateContent(prompt);
    const generatedText = await result.response.text();  // Await to get the response text
    rawText = generatedText.trim();  // Remove any leading/trailing whitespace
    rawText = rawText.replace(/^"|"$/g, ''); // Remove any surrounding double quotes if present
    console.log(rawText)
    // Attempt to parse the cleaned text into JSON
    // const jsonResponse = JSON.parse(rawText); 
    
    // If the result is already a JSON format (as expected), parse it
    // const jsonResponse = JSON.parse(generatedText);

    res.json(rawText);  // Return the JSON response
  } catch (error) {
    console.error("Error generating text:", error);
    res.status(500).json({ error: "Error generating text" });
  }
});

app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});

```

### scripting/index.js

```javascript
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

const DYNAMIC_SCRIPT_ID = 'dynamic-script';

async function isDynamicContentScriptRegistered() {
  const scripts = await chrome.scripting.getRegisteredContentScripts();
  return scripts.some((s) => s.id === DYNAMIC_SCRIPT_ID);
}

document
  .querySelector('#inject-programmatic')
  .addEventListener('click', async () => {
    // Unregister the dynamic content script to avoid multiple injections.
    const dynamicContentScriptRegistered =
      await isDynamicContentScriptRegistered();

    if (dynamicContentScriptRegistered) {
      await chrome.scripting.unregisterContentScripts({
        ids: [DYNAMIC_SCRIPT_ID]
      });
    }

    // Now, execute the script. We handle this in the service worker so we can
    // wait for the tab to open and **then** inject our script.
    const world = document.querySelector("[name='world']").value;
    chrome.runtime.sendMessage({
      name: 'inject-programmatic',
      options: { world }
    });
  });

document
  .querySelector('#register-dynamic')
  .addEventListener('click', async () => {
    const persistAcrossSessions =
      document.querySelector("[name='persist']").value === 'yes';
    const matches = document.querySelector("[name='matches']").value;
    const runAt = document.querySelector("[name='run-at']").value;
    const allFrames =
      document.querySelector("[name='all-frames']").value === 'yes';
    const world = document.querySelector("[name='world']").value;

    await chrome.scripting.registerContentScripts([
      {
        id: DYNAMIC_SCRIPT_ID,
        js: ['content-script.js'],
        persistAcrossSessions,
        matches: [matches],
        runAt,
        allFrames,
        world
      }
    ]);

    // Only open the page by default if the `matches` field hasn't been changed.
    if (matches === 'https://example.com/*') {
      await chrome.tabs.create({ url: 'https://example.com' });
    }

    updateUI();
  });

document
  .querySelector('#unregister-dynamic')
  .addEventListener('click', async () => {
    await chrome.scripting.unregisterContentScripts({
      ids: [DYNAMIC_SCRIPT_ID]
    });
    updateUI();
  });

const PROGRAMMATIC_TAB_SELECTOR = "[name='type'][value='programmatic']";
const DYNAMIC_TAB_SELECTOR = "[name='type'][value='dynamic']";

function updateUI() {
  const type = document.querySelector(PROGRAMMATIC_TAB_SELECTOR).checked
    ? 'programmatic'
    : 'dynamic';

  // Update selected tab.
  document.querySelector(PROGRAMMATIC_TAB_SELECTOR).parentElement.className =
    type === 'programmatic' ? 'selected' : '';
  document.querySelector(DYNAMIC_TAB_SELECTOR).parentElement.className =
    type === 'dynamic' ? 'selected' : '';

  // Only show some fields for dynamic scripts.
  document.querySelector("[name='run-at']").parentElement.style.display =
    type === 'dynamic' ? '' : 'none';
  document.querySelector("[name='persist']").parentElement.style.display =
    type === 'dynamic' ? '' : 'none';
  document.querySelector("[name='all-frames']").parentElement.style.display =
    type === 'dynamic' ? '' : 'none';
  document.querySelector("[name='matches']").parentElement.style.display =
    type === 'dynamic' ? '' : 'none';
  document.querySelector('.hint').style.display =
    type === 'dynamic' ? '' : 'none';

  // Update visible buttons.
  document.querySelector('.programmatic-buttons').style.display =
    type === 'programmatic' ? 'flex' : 'none';
  document.querySelector('.dynamic-buttons').style.display =
    type === 'dynamic' ? 'flex' : 'none';

  // Decide if the register or unregister button is visible for dynamic scripts.
  isDynamicContentScriptRegistered().then((dynamicContentScriptRegistered) => {
    document
      .querySelector('#register-dynamic')
      .toggleAttribute('disabled', dynamicContentScriptRegistered);
    document
      .querySelector('#unregister-dynamic')
      .toggleAttribute('disabled', !dynamicContentScriptRegistered);
  });
}

updateUI();

document
  .querySelector(PROGRAMMATIC_TAB_SELECTOR)
  .addEventListener('change', (e) => {
    e.target.checked && updateUI();
  });

document.querySelector(DYNAMIC_TAB_SELECTOR).addEventListener('change', (e) => {
  e.target.checked && updateUI();
});

```

### reference.mv3-content-scripts/content-script.js

```javascript
alert('File test alert');

```

### reference.mv3-content-scripts/popup.css

```css
* {
  box-sizing: border-box;
}
html,
body,
main {
  height: 100%;
  margin: 0;
  padding: 0;
}
body {
  min-width: 20em;
  min-height: 10em;
}
main {
  padding: 1em 0.5em;
  display: grid;
  place-items: center;
}

```

### userScripts/styles.css

```css
#suggestion-box {
    background-color: white;
    border: 1px solid #ccc;
    padding: 10px;
    width: 250px;
    display: none;
    position: absolute;
    z-index: 1000;
  }
  
  #suggestion-box p {
    margin: 5px 0;
    padding: 5px;
    background-color: #f1f1f1;
    cursor: pointer;
  }
  
  #suggestion-box p:hover {
    background-color: #e0e0e0;
  }
  
```

### reference.mv3-content-scripts/popup.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="popup.css" />
    <script src="popup.js" defer></script>
  </head>
  <body>
    <main>
      <div>
        <button id="inject-file">Inject file</button>
      </div>
      <div>
        <button id="inject-function">Inject function</button>
      </div>
    </main>
  </body>
</html>

```

### reference.mv3-content-scripts/popup.js

```javascript
const injectFile = document.getElementById('inject-file');
const injectFunction = document.getElementById('inject-function');

async function getCurrentTab() {
  const queryOptions = { active: true, currentWindow: true };
  const [tab] = await chrome.tabs.query(queryOptions);
  return tab;
}

injectFile.addEventListener('click', async () => {
  const tab = await getCurrentTab();

  chrome.scripting.executeScript({
    target: { tabId: tab.id },
    files: ['content-script.js']
  });
});

function showAlert(givenName) {
  alert(`Hello, ${givenName}`);
}

injectFunction.addEventListener('click', async () => {
  const tab = await getCurrentTab();

  const name = 'World';
  chrome.scripting.executeScript({
    target: { tabId: tab.id },
    func: showAlert,
    args: [name]
  });
});

```

### scripting/sw.js

```javascript
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

chrome.action.onClicked.addListener(openDemoTab);

function openDemoTab() {
  chrome.tabs.create({ url: 'index.html' });
}

chrome.webNavigation.onDOMContentLoaded.addListener(async ({ tabId, url }) => {
  if (url !== 'https://chatgpt.com/*') return;
  const { options } = await chrome.storage.local.get('options');
  chrome.scripting.executeScript({
    target: { tabId },
    files: ['content-script.js'],
    ...options
  });
});

chrome.runtime.onMessage.addListener(async ({ name, options }) => {
  if (name === 'inject-programmatic') {
    await chrome.storage.local.set({ options });
    await chrome.tabs.create({
      url: 'https://chatgpt.com/*'
    });
  }
});

```

### scripting/index.css

```css
/* Copyright 2023 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

form {
  display: inline-block;
  border: 1px solid #dadce0;
  min-width: 500px;
}

h1 {
  background: #f6f9fe;
  margin: 0;
  padding: 15px;
  font-size: 20px;
  text-align: center;
}

.tabs {
  border-top: 1px solid #dadce0;
  border-bottom: 1px solid #dadce0;
  display: flex;
}

.tabs label:first-child {
  border-right: 1px solid grey;
}

.tabs label.selected {
  background: white;
  font-weight: bold;
}

.tabs label {
  display: block;
  background: #f6f9fe;
  text-align: center;
  width: 100%;
  padding: 7px 0;
  margin: 0;
}

.tabs input {
  display: none;
}

label {
  margin: 20px;
  display: flex;
  justify-content: space-between;
}

.hint {
  margin: 0 20px;
  background: #f6f6f6;
  padding: 7px 12px;
}

.buttons {
  display: flex;
  margin: 20px;
}

.buttons button {
  margin-right: 15px;
}

```

[5 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]