├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── demo ├── java-class │ ├── english-compiler.jsonc │ ├── prompt_cache │ │ └── codegen │ │ │ ├── java │ │ │ ├── CodeExtraction.OAuth2ProviderHelper.jsonl │ │ │ ├── CodeMinify.OAuth2ProviderHelper.jsonl │ │ │ ├── MethodCodeDep.OAuth2ProviderHelper.jsonl │ │ │ ├── MethodCodeG1.OAuth2ProviderHelper.jsonl │ │ │ ├── MethodCodeG2.OAuth2ProviderHelper.jsonl │ │ │ ├── MethodCodeG3.OAuth2ProviderHelper.jsonl │ │ │ ├── MinifiedBaseCodeG1.OAuth2ProviderHelper.jsonl │ │ │ ├── MinifiedBaseCodeG2.OAuth2ProviderHelper.jsonl │ │ │ ├── SpecChangeSuggestion.OAuth2ProviderHelper.jsonl │ │ │ ├── SpecClassCommentG1.OAuth2ProviderHelper.jsonl │ │ │ ├── SpecClassCommentG2.OAuth2ProviderHelper.jsonl │ │ │ ├── SpecClassCommentG3.OAuth2ProviderHelper.jsonl │ │ │ ├── SpecClassProperty.OAuth2ProviderHelper.jsonl │ │ │ └── personality-remark.OAuth2ProviderHelper.jsonl │ │ │ └── snippet │ │ │ └── CodeExtraction.VariousSnippet.jsonl │ ├── spec │ │ └── OAuth2ProviderHelper.spec.md │ └── src │ │ └── OAuth2ProviderHelper.java └── twitter-clone │ ├── english-compiler.jsonc │ ├── package-lock.json │ ├── package.json │ ├── prompt_cache │ └── codegen │ │ ├── html │ │ ├── FunctionModuleCodegen.index.jsonl │ │ ├── SpecChangeSuggestion.index.jsonl │ │ ├── SpecChangeUsingFeedback.index.jsonl │ │ └── personality-remark.index.jsonl │ │ ├── java │ │ └── personality-remark.README.jsonl │ │ └── javascript │ │ ├── FunctionModuleCodegen.addTweet.jsonl │ │ ├── FunctionModuleCodegen.db.jsonl │ │ ├── FunctionModuleCodegen.listTweets.jsonl │ │ ├── FunctionModuleCodegen.server.jsonl │ │ ├── SpecChangeSuggestion.db.jsonl │ │ └── personality-remark.db.jsonl │ ├── spec │ ├── README.md │ ├── core │ │ └── db.spec.md │ ├── handler │ │ ├── addTweet.spec.md │ │ └── listTweets.spec.md │ ├── server.spec.md │ └── ui │ │ └── index.spec.md │ ├── src │ ├── core │ │ └── db.js │ ├── handler │ │ ├── addTweet.js │ │ └── listTweets.js │ ├── server.js │ └── ui │ │ └── index.html │ └── test │ └── core │ └── db.test.js ├── notes ├── imgs │ ├── CLI-build-all.png │ ├── CLI-build-one.png │ ├── CLI-spec-suggest.png │ ├── EnglishCommand-CLI-help.png │ ├── demo-code-files.png │ ├── demo-spec-files.png │ ├── example-spec-to-code.png │ ├── large-spec-build-process.png │ ├── oauth2-spec-and-code.png │ ├── twitter-db-code.png │ ├── twitter-db-spec.png │ └── youtube-thumbnail.png └── pitch │ ├── v1.md │ ├── v2.md │ └── v3.md ├── package-lock.json ├── package.json └── src ├── EnglishCompiler.js ├── cli ├── MainSywac.js ├── OutputHandler.js ├── build │ ├── all.js │ └── file.js ├── spec │ ├── infer.js │ └── suggest.js └── util │ └── getSpecFile.js ├── codegen ├── html │ └── specToCodeFile.js ├── javascript │ ├── specToFunctionModule.js │ └── specToFunctionModuleUnitTest.js ├── markdownSpecFileChangeWithFeedback.js ├── markdownSpecFileSuggestion.js ├── markdownSpecFileToCode.js ├── markdownSpecToCode.js ├── personalityRemark.js ├── promptParts │ ├── blockWrapper.js │ ├── cachePath.js │ ├── fullSpecPrefix.js │ ├── getAgentLabels.js │ ├── normalizeSpecObj.js │ ├── removeSuffixIfPresent.js │ └── simpleAndSafeMinify.js ├── prototype │ ├── codeConversion.js │ ├── codeExtraction.js │ ├── codeMinify.js │ ├── methodSpecToCodeSet.js │ ├── specExtraction.js │ ├── specQuestionAndAnswer.js │ ├── specToClassCommentBlock.js │ ├── specToClassPropertyWithComments.js │ ├── specToCodeClassSkeleton.js │ ├── specToFullClassCode.js │ └── specToMinifiedBaseCode.js └── segment │ ├── specChangeSuggestion.js │ ├── specChangeUsingFeedback.js │ └── specExtraction.js ├── core └── config.js ├── debug └── promptAndAnswer.js ├── openai ├── cachedCompletion.js ├── getApiKey.js ├── getTokenCount.js └── promptToCompletion.js ├── util ├── fileExist.js ├── getMarkdownObj.js ├── indentString.js └── scanDirForFiles.js └── version.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore system hidden files, and node modules 2 | .* 3 | node_modules 4 | 5 | # Ignore the dev config 6 | config.json 7 | config.jsonc 8 | *.jsonc 9 | 10 | # Include back git files 11 | !.git* 12 | # Add back npmignore 13 | !.npmignore 14 | 15 | # Include the demo config 16 | !demo/**/english-compiler.jsonc -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | dist-repos/ 3 | node_modules/ 4 | .git/ 5 | .* 6 | notes/ 7 | demo/ 8 | config.jsonc 9 | *.jsonc 10 | **/*.jsonc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 Eugene Cheah (@picocreator) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /demo/java-class/english-compiler.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | // Openai API key 3 | "openai_apikey": "REPLACE-WITH-YOUR-ACTUAL-KEY-AND-DO-NOT-CHECKIN", 4 | 5 | // Prompt caching dir, this can also be used to build up a historical dataset 6 | // 7 | // This is preconfigured, to allow you to quickly run the demo. However once you make changes 8 | // to the specification, you will need to add the openai_apikey 9 | "prompt_cache_dir": "./prompt_cache", 10 | 11 | // Specification directory 12 | "spec_dir": "./spec", 13 | 14 | // Source code output directory 15 | "code_dir": "./src", 16 | 17 | // Test code output directory 18 | "test_dir": "./test", 19 | 20 | // Add some personality based remarks 21 | "personality": "Sassy & Sarcastic" 22 | } -------------------------------------------------------------------------------- /demo/java-class/prompt_cache/codegen/java/CodeExtraction.OAuth2ProviderHelper.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"Extract from the following\n\njava code :\n```\npublic class OAuth2ProviderHelper{private String authorizationURL,scope,tokenURL,redirectURL,clientID,clientSecret,identityURL,identityProperty;public OAuth2ProviderHelper(Map params){this.authorizationURL=(String)params.get(\"authorizationURL\");this.scope=(String)params.get(\"scope\");this.tokenURL=(String)params.get(\"tokenURL\");this.redirectURL=(String)params.get(\"redirectURL\");this.clientID=(String)params.get(\"clientID\");this.clientSecret=(String)params.get(\"clientSecret\");this.identityURL=(String)params.get(\"identityURL\");this.identityProperty=(String)params.get(\"identityProperty\");if(this.identityProperty==null)this.identityProperty=\"email\";}public String getAuthorizationSessionUrl(){try{return this.authorizationURL+\"?client_id=\"+this.clientID+\"&redirect_uri=\"+URLEncoder.encode(this.redirectURL,\"UTF-8\")+\"&scope=\"+this.scope;}catch(UnsupportedEncodingException e){throw new RuntimeException(e);}}public String getValidAccessToken(String code){Map reqParams=new HashMap();reqParams.put(\"code\",code);reqParams.put(\"client_id\",this.clientID);reqParams.put(\"client_secret\",this.clientSecret);reqParams.put(\"redirect_uri\",this.redirectURL);HttpResponse response=RequestHttp.postJSON(this.tokenURL,reqParams);Map resultMap=response.toMap();return (String)resultMap.get(\"access_token\");}public String getIdentity(String token){Map reqParams=new HashMap();Map reqHeaders=new HashMap();reqHeaders.put(\"Authorization\",\"Bearer \"+token);HttpResponse response=RequestHttp.postJSON(this.identityURL,reqParams,null,reqHeaders);Map resultMap=response.toMap();return (String)resultMap.get(this.identityProperty);}}\n```\n\nThe list of method names (including the constructor), without the parameters or brackets. As a JSON array. If there is a class constructor, return it as the first item\n\n```\n","completion":"[\"OAuth2ProviderHelper\",\"getAuthorizationSessionUrl\",\"getValidAccessToken\",\"getIdentity\"]\n``"} 2 | {"prompt":"Extract from the following\n\njava snippet code, containing package namespace (if any) and dependencies declaration :\n```\npackage OAuth2ProviderHelper;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.net.URLEncoder;\nimport java.io.UnsupportedEncodingException;\nimport picoded.core.web.RequestHttp;\nimport picoded.core.web.ResponseHttp;\nimport java.net.URLEncoder;\nimport picoded.core.web.RequestHttp;\nimport picoded.core.web.ResponseHttp;\nimport java.net.URLEncoder;\nimport picoded.core.web.RequestHttp;\nimport picoded.core.web.ResponseHttp;\n```\n\nThe Package namespace (if any), and all the dependencies declarations, with any duplicates removed, and redundant blank lines removed.\n\n```\n","completion":"package OAuth2ProviderHelper;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.util.HashMap;\nimport java.util.Map;\nimport picoded.core.web.RequestHttp;\nimport picoded.core.web.ResponseHttp;\n```"} 3 | -------------------------------------------------------------------------------- /demo/java-class/prompt_cache/codegen/java/SpecChangeSuggestion.OAuth2ProviderHelper.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe java class can be described with the following details\n\nclass name: OAuth2ProviderHelper\nDocumentation Style: JavaDoc\n\nclass textual description (between the `<<{{~~}}>>` lines) :\n<<{{~~}}>>\n# OAuth2ProviderIntegration Class\n\nOAuth2ProviderIntegration is used to simplify the integration of oauth2 for an existing java api servers, with an existing provider. This class provides an easy way to authenticate users and get their identity, as well as to provide a secure authentication system.\n\nThis helps facilitate the following flow between a client (via a browser), a server (the server program using this class), and a provider (such as github or google).\n\n1. The client sends a request to the server to initiate the OAuth 2.0 flow.\n2, The server responds by redirecting the client to the identity provider's authorization endpoint. This redirect includes a unique authorization code, which the identity provider uses to identify the client and the server.\n3. The client is redirected to the identity provider's login page, where the user is asked to log in and grant the client access to the requested resources.\n4. If the user grants access, the identity redirects the client to the server (redirectURL), which includes the authorization code.\n5. The client sends the authorization code to the server,\n6. The server exchanges the authorization code for an access token\n7. The server uses the access token, to get the client unique identity\n8. The server validate the identity, and logs the user into the server application (outside the scope of this class)\n\n---\n\nThe following is the private string properties which is initialized by the constructor:\n- authorizationURL : oauth2 \"/auth\" endpoint, can include GET parameters\n- scope : Scope parameter to be passed with the authorization URL\n- tokenURL : oauth2 \"/token\" endpoint, can include get parameters\n- redirectURL : redirect URL, to send the user back to, after authorization. This is used by the oauth2 provider, to redirect the user with a valid code after login.\n- clientID : oauth2 client ID, used together with the authorizationURL to redirect the user for login via the browser\n- clientSecret : oauth2 client secret, used to perform server to server communication with the oauth provider, to get the valid access token.\n- identityURL : JSON API endpoint from the provider, to get the unique login ID (eg. email). This is used by the getIdentity method. And is called using a POST request.\n- identityProperty : Property returned by the identityURL, for the unique login ID, defaults to \"email\"\n\nPublic methods:\n- getAuthorizationSessionUrl() : Returns the authorization session URL (String), this should be used to redirect the user.\n- getValidAccessToken(code) : Returns a valid access token (String), requires the response code given to user when they are redirected back to redirectURL\n- getIdentity(token) : Returns the identity of the user (String)\n\nImplementation notes:\n\nAny exceptions thrown by the class will be of type RuntimeException.\n\nImport and use the 'picoded.core.web.RequestHttp' and 'picoded.core.web.ResponseHttp', for performing HTTP API request\nThe following is an example of how to use the RequestHttp API\n\n```\n// Perform a GET request\nHttpResponse response = RequestHttp.get( requestURL );\n\n// Alternatively a POST request as a JSON request, with parameters\nMap requestParams = new HashMap();\nrequestParams.put(\"test\",\"one\");\nresponse = RequestHttp.postJSON( requestURL, requestParams );\n\n// Get the result string\nString getResultString = response.toString();\n\n// Or the response code\nint statusCode = response.statusCode();\n\n// Or a string object map\nMap resultMap = response.toMap();\n```\n\n---\n\n## Method Specific Specification\n\n### constructor\n\nThe constructor accepts the following parameters, which would be provided in a java Map:\n- authorizationURL (String)\n- tokenURL (String)\n- redirectURL (String)\n- clientID (String)\n- clientSecret (String)\n- identityURL (String)\n- identityProperty (String, optional)\n\n### getAuthorizationSessionUrl\n\nThe getAuthorizationSessionUrl method returns a String containing the authorization session URL.\n\nImplementation note: Do ensure to URI encode the redirectURL, when generating the session URL\n\nYou will need to handle both situations where the 'authorizationURL'\n- contains a GET parameters (eg. http://example.com/oauth2/auth?param=something)\n- contains no GET parameters (eg. http://example.com/oauth2/auth)\n\nIf the 'authorizationURL' is not a valid URL format, throw an error accordingly.\n\n### getValidAccessToken\n\nThe getValidAccessToken method returns a valid access token as a String.\n\n### getIdentity\n\nThe getIdentity method returns the identity of the user as a String.\nFor 'getIdentity', perform the request using the token with basic authentication. Headers can be set for RequestHttp, similar to the following example.\n\n```\n// Alternatively a POST request as a JSON request, with parameters\nMap reqParams = new HashMap();\nreqParams.put(\"test\",\"one\");\n\n// Custom cookies\nMap reqCookie = new HashMap();\nreqCookie.put(\"cookie_a\",\"b\");\n\n// Custom headers\nMap reqHeaders = new HashMap();\nreqHeaders.put(\"custom_header\",\"c\");\n\n// Request with cookie and header\n// note that any argument can be null, if the parameter is not needed\nHttpResponse response = RequestHttp.postJSON( requestURL, reqParams, reqCookie, reqHeaders );\n```\n<<{{~~}}>>\n\ndeveloper:\nPlease suggest changes, to improve the specification. In point form.\nReply with NO_SUGGESTION if there is no suggested changes, to improve the specification.\n\nAI:\n","completion":"NO_SUGGESTION"} 2 | -------------------------------------------------------------------------------- /demo/java-class/prompt_cache/codegen/java/SpecClassCommentG1.OAuth2ProviderHelper.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"developer:\nFor the following java minified code\n\njava code :\n```\npublic class OAuth2ProviderHelper{private String authorizationURL,scope,tokenURL,redirectURL,clientID,clientSecret,identityURL,identityProperty;public OAuth2ProviderHelper(Map params){this.authorizationURL=(String)params.get(\"authorizationURL\");this.scope=(String)params.get(\"scope\");this.tokenURL=(String)params.get(\"tokenURL\");this.redirectURL=(String)params.get(\"redirectURL\");this.clientID=(String)params.get(\"clientID\");this.clientSecret=(String)params.get(\"clientSecret\");this.identityURL=(String)params.get(\"identityURL\");this.identityProperty=(String)params.get(\"identityProperty\");if(this.identityProperty==null)this.identityProperty=\"email\";}public String getAuthorizationSessionUrl(){try{return this.authorizationURL+\"?client_id=\"+this.clientID+\"&redirect_uri=\"+URLEncoder.encode(this.redirectURL,\"UTF-8\")+\"&scope=\"+this.scope;}catch(UnsupportedEncodingException e){throw new RuntimeException(e);}}public String getValidAccessToken(String code){Map reqParams=new HashMap();reqParams.put(\"code\",code);reqParams.put(\"client_id\",this.clientID);reqParams.put(\"client_secret\",this.clientSecret);reqParams.put(\"redirect_uri\",this.redirectURL);HttpResponse response=RequestHttp.postJSON(this.tokenURL,reqParams);Map resultMap=response.toMap();return (String)resultMap.get(\"access_token\");}public String getIdentity(String token){Map reqParams=new HashMap();Map reqHeaders=new HashMap();reqHeaders.put(\"Authorization\",\"Bearer \"+token);HttpResponse response=RequestHttp.postJSON(this.identityURL,reqParams,null,reqHeaders);Map resultMap=response.toMap();return (String)resultMap.get(this.identityProperty);}}\n```\n\nPlease generate the OAuth2ProviderHelper class top level comment, as a comment block like the following\n\n```\n/**\n * Detailed class comment here\n */\n```\n\nReturn only the comment block.\n\nAI:\n```\n","completion":"/**\n * OAuth2ProviderHelper class provides methods to get authorization session URL, valid access token and identity.\n */"} 2 | -------------------------------------------------------------------------------- /demo/java-class/prompt_cache/codegen/java/SpecClassCommentG3.OAuth2ProviderHelper.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"The following is the comment block segment for the OAuth2ProviderHelper class / interface\n\n```\n/**\n * OAuth2ProviderHelper is a utility class used to simplify the integration of OAuth2 authentication with existing Java API servers.\n * It provides an easy way to authenticate users and get their identity, as well as to provide a secure authentication system.\n * \n * This class helps facilitate the following flow between a client (via a browser), a server (the server program using this class), and a provider (such as Github or Google):\n * \n * 1. The client sends a request to the server to initiate the OAuth 2.0 flow.\n * 2. The server responds by redirecting the client to the identity provider's authorization endpoint. This redirect includes a unique authorization code, which the identity provider uses to identify the client and the server.\n * 3. The client is redirected to the identity provider's login page, where the user is asked to log in and grant the client access to the requested resources.\n * 4. If the user grants access, the identity redirects the client to the server (redirectURL), which includes the authorization code.\n * 5. The client sends the authorization code to the server.\n * 6. The server exchanges the authorization code for an access token.\n * 7. The server uses the access token to get the client's unique identity.\n * 8. The server validates the identity and logs the user into the server application (outside the scope of this class).\n * \n * This class provides the following public methods:\n * \n * - getAuthorizationSessionUrl(): Returns the authorization session URL (String), this should be used to redirect the user.\n * - getValidAccessToken(code): Returns a valid access token (String), requires the response code given to user when they are redirected back to redirectURL.\n * - getIdentity(token): Returns the identity of the user (String).\n * \n * Any exceptions thrown by the class will be of type RuntimeException.\n */\n```\n\ndeveloper:\nPlease rewrite the OAuth2ProviderHelper comment block, for improved readability.\n\nReturn only the comment block.\n\nAI:\n```\n","completion":"/**\n * OAuth2ProviderHelper is a utility class used to simplify the integration of OAuth2 authentication with existing Java API servers.\n * It provides an easy way to authenticate users and get their identity, as well as to provide a secure authentication system.\n * \n * This class helps facilitate the following flow between a client (via a browser), a server (the server program using this class), and a provider (such as Github or Google):\n * \n * 1. The client sends a request to the server to initiate the OAuth 2.0 flow.\n * 2. The server responds by redirecting the client to the identity provider's authorization endpoint, including a unique authorization code.\n * 3. The client is redirected to the identity provider's login page, where the user is asked to log in and grant the client access.\n * 4. If the user grants access, the identity redirects the client to the server (redirectURL) with the authorization code.\n * 5. The client sends the authorization code to the server.\n * 6. The server exchanges the authorization code for an access token.\n * 7. The server uses the access token to get the client's unique identity.\n * 8. The server validates the identity and logs the user into the server application (outside the scope of this class).\n * \n * This class provides the following public methods:\n * \n * - getAuthorizationSessionUrl(): Returns the authorization session URL (String), used to redirect the user.\n * - getValidAccessToken(code): Returns a valid access token (String), requires the response code given to user when they are redirected back.\n * - getIdentity(token): Returns the identity of the user (String).\n * \n * Any exceptions thrown by the class will be of type RuntimeException.\n */\n```"} 2 | -------------------------------------------------------------------------------- /demo/java-class/prompt_cache/codegen/java/SpecClassProperty.OAuth2ProviderHelper.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe java class can be described with the following details\n\nclass name: OAuth2ProviderHelper\nDocumentation Style: JavaDoc\n\nclass textual description (between the `<<{{~~}}>>` lines) :\n<<{{~~}}>>\n# OAuth2ProviderIntegration Class\n\nOAuth2ProviderIntegration is used to simplify the integration of oauth2 for an existing java api servers, with an existing provider. This class provides an easy way to authenticate users and get their identity, as well as to provide a secure authentication system.\n\nThis helps facilitate the following flow between a client (via a browser), a server (the server program using this class), and a provider (such as github or google).\n\n1. The client sends a request to the server to initiate the OAuth 2.0 flow.\n2, The server responds by redirecting the client to the identity provider's authorization endpoint. This redirect includes a unique authorization code, which the identity provider uses to identify the client and the server.\n3. The client is redirected to the identity provider's login page, where the user is asked to log in and grant the client access to the requested resources.\n4. If the user grants access, the identity redirects the client to the server (redirectURL), which includes the authorization code.\n5. The client sends the authorization code to the server,\n6. The server exchanges the authorization code for an access token\n7. The server uses the access token, to get the client unique identity\n8. The server validate the identity, and logs the user into the server application (outside the scope of this class)\n\n---\n\nThe following is the private string properties which is initialized by the constructor:\n- authorizationURL : oauth2 \"/auth\" endpoint, can include GET parameters\n- scope : Scope parameter to be passed with the authorization URL\n- tokenURL : oauth2 \"/token\" endpoint, can include get parameters\n- redirectURL : redirect URL, to send the user back to, after authorization. This is used by the oauth2 provider, to redirect the user with a valid code after login.\n- clientID : oauth2 client ID, used together with the authorizationURL to redirect the user for login via the browser\n- clientSecret : oauth2 client secret, used to perform server to server communication with the oauth provider, to get the valid access token.\n- identityURL : JSON API endpoint from the provider, to get the unique login ID (eg. email). This is used by the getIdentity method. And is called using a POST request.\n- identityProperty : Property returned by the identityURL, for the unique login ID, defaults to \"email\"\n\nPublic methods:\n- getAuthorizationSessionUrl() : Returns the authorization session URL (String), this should be used to redirect the user.\n- getValidAccessToken(code) : Returns a valid access token (String), requires the response code given to user when they are redirected back to redirectURL\n- getIdentity(token) : Returns the identity of the user (String)\n\nImplementation notes:\n\nAny exceptions thrown by the class will be of type RuntimeException.\n\nImport and use the 'picoded.core.web.RequestHttp' and 'picoded.core.web.ResponseHttp', for performing HTTP API request\nThe following is an example of how to use the RequestHttp API\n\n```\n// Perform a GET request\nHttpResponse response = RequestHttp.get( requestURL );\n\n// Alternatively a POST request as a JSON request, with parameters\nMap requestParams = new HashMap();\nrequestParams.put(\"test\",\"one\");\nresponse = RequestHttp.postJSON( requestURL, requestParams );\n\n// Get the result string\nString getResultString = response.toString();\n\n// Or the response code\nint statusCode = response.statusCode();\n\n// Or a string object map\nMap resultMap = response.toMap();\n```\n\n---\n\n## Method Specific Specification\n\n### constructor\n\nThe constructor accepts the following parameters, which would be provided in a java Map:\n- authorizationURL (String)\n- tokenURL (String)\n- redirectURL (String)\n- clientID (String)\n- clientSecret (String)\n- identityURL (String)\n- identityProperty (String, optional)\n\n### getAuthorizationSessionUrl\n\nThe getAuthorizationSessionUrl method returns a String containing the authorization session URL.\n\nImplementation note: Do ensure to URI encode the redirectURL, when generating the session URL\n\nYou will need to handle both situations where the 'authorizationURL'\n- contains a GET parameters (eg. http://example.com/oauth2/auth?param=something)\n- contains no GET parameters (eg. http://example.com/oauth2/auth)\n\nIf the 'authorizationURL' is not a valid URL format, throw an error accordingly.\n\n### getValidAccessToken\n\nThe getValidAccessToken method returns a valid access token as a String.\n\n### getIdentity\n\nThe getIdentity method returns the identity of the user as a String.\nFor 'getIdentity', perform the request using the token with basic authentication. Headers can be set for RequestHttp, similar to the following example.\n\n```\n// Alternatively a POST request as a JSON request, with parameters\nMap reqParams = new HashMap();\nreqParams.put(\"test\",\"one\");\n\n// Custom cookies\nMap reqCookie = new HashMap();\nreqCookie.put(\"cookie_a\",\"b\");\n\n// Custom headers\nMap reqHeaders = new HashMap();\nreqHeaders.put(\"custom_header\",\"c\");\n\n// Request with cookie and header\n// note that any argument can be null, if the parameter is not needed\nHttpResponse response = RequestHttp.postJSON( requestURL, reqParams, reqCookie, reqHeaders );\n```\n<<{{~~}}>>\n\nThe following is the various property declaration for the OAuth2ProviderHelper class / interface\n\n```\nprivate String authorizationURL,scope,tokenURL,redirectURL,clientID,clientSecret,identityURL,identityProperty;\n```\n\ndeveloper:\nAdd comments to the OAuth2ProviderHelper property declarations.\nReturn the property declarations with comments.\n\nAI:\n```\n","completion":"// oauth2 \"/auth\" endpoint, can include GET parameters\nprivate String authorizationURL;\n\n// Scope parameter to be passed with the authorization URL\nprivate String scope;\n\n// oauth2 \"/token\" endpoint, can include get parameters\nprivate String tokenURL;\n\n// redirect URL, to send the user back to, after authorization. This is used by the oauth2 provider, to redirect the user with a valid code after login.\nprivate String redirectURL;\n\n// oauth2 client ID, used together with the authorizationURL to redirect the user for login via the browser\nprivate String clientID;\n\n// oauth2 client secret, used to perform server to server communication with the oauth provider, to get the valid access token.\nprivate String clientSecret;\n\n// JSON API endpoint from the provider, to get the unique login ID (eg. email). This is used by the getIdentity method. And is called using a POST request.\nprivate String identityURL;\n\n// Property returned by the identityURL, for the unique login ID, defaults to \"email\"\nprivate String identityProperty;\n```"} 2 | -------------------------------------------------------------------------------- /demo/java-class/prompt_cache/codegen/snippet/CodeExtraction.VariousSnippet.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"Extract from the following\n\njava code :\n```\npublic class OAuth2ProviderHelper{private String authorizationURL,scope,tokenURL,redirectURL,clientID,clientSecret,identityURL,identityProperty;public OAuth2ProviderHelper(Map params){this.authorizationURL=(String)params.get(\"authorizationURL\");this.scope=(String)params.get(\"scope\");this.tokenURL=(String)params.get(\"tokenURL\");this.redirectURL=(String)params.get(\"redirectURL\");this.clientID=(String)params.get(\"clientID\");this.clientSecret=(String)params.get(\"clientSecret\");this.identityURL=(String)params.get(\"identityURL\");this.identityProperty=(String)params.get(\"identityProperty\");if(this.identityProperty==null)this.identityProperty=\"email\";}public String getAuthorizationSessionUrl(){try{return this.authorizationURL+\"?client_id=\"+this.clientID+\"&redirect_uri=\"+URLEncoder.encode(this.redirectURL,\"UTF-8\")+\"&scope=\"+this.scope;}catch(UnsupportedEncodingException e){throw new RuntimeException(e);}}public String getValidAccessToken(String code){Map reqParams=new HashMap();reqParams.put(\"code\",code);reqParams.put(\"client_id\",this.clientID);reqParams.put(\"client_secret\",this.clientSecret);reqParams.put(\"redirect_uri\",this.redirectURL);HttpResponse response=RequestHttp.postJSON(this.tokenURL,reqParams);Map resultMap=response.toMap();return (String)resultMap.get(\"access_token\");}public String getIdentity(String token){Map reqParams=new HashMap();Map reqHeaders=new HashMap();reqHeaders.put(\"Authorization\",\"Bearer \"+token);HttpResponse response=RequestHttp.postJSON(this.identityURL,reqParams,null,reqHeaders);Map resultMap=response.toMap();return (String)resultMap.get(this.identityProperty);}}\n```\n\nThe Package namespace (if any), and all the dependencies declarations (if any), return only as package and import statements\n\n```\n","completion":"package OAuth2ProviderHelper;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.util.HashMap;\nimport java.util.Map;\n``"} 2 | {"prompt":"Extract from the following\n\njava code :\n```\npublic class OAuth2ProviderHelper{private String authorizationURL,scope,tokenURL,redirectURL,clientID,clientSecret,identityURL,identityProperty;public OAuth2ProviderHelper(Map params){this.authorizationURL=(String)params.get(\"authorizationURL\");this.scope=(String)params.get(\"scope\");this.tokenURL=(String)params.get(\"tokenURL\");this.redirectURL=(String)params.get(\"redirectURL\");this.clientID=(String)params.get(\"clientID\");this.clientSecret=(String)params.get(\"clientSecret\");this.identityURL=(String)params.get(\"identityURL\");this.identityProperty=(String)params.get(\"identityProperty\");if(this.identityProperty==null)this.identityProperty=\"email\";}public String getAuthorizationSessionUrl(){try{return this.authorizationURL+\"?client_id=\"+this.clientID+\"&redirect_uri=\"+URLEncoder.encode(this.redirectURL,\"UTF-8\")+\"&scope=\"+this.scope;}catch(UnsupportedEncodingException e){throw new RuntimeException(e);}}public String getValidAccessToken(String code){Map reqParams=new HashMap();reqParams.put(\"code\",code);reqParams.put(\"client_id\",this.clientID);reqParams.put(\"client_secret\",this.clientSecret);reqParams.put(\"redirect_uri\",this.redirectURL);HttpResponse response=RequestHttp.postJSON(this.tokenURL,reqParams);Map resultMap=response.toMap();return (String)resultMap.get(\"access_token\");}public String getIdentity(String token){Map reqParams=new HashMap();Map reqHeaders=new HashMap();reqHeaders.put(\"Authorization\",\"Bearer \"+token);HttpResponse response=RequestHttp.postJSON(this.identityURL,reqParams,null,reqHeaders);Map resultMap=response.toMap();return (String)resultMap.get(this.identityProperty);}}\n```\n\nThe class declaration line, excluding the opening bracket '{', excluding dependencies, excluding methods and properties, excluding closing bracket '}'\n\n```\n","completion":"public class OAuth2ProviderHelper"} 3 | {"prompt":"Extract from the following\n\njava code :\n```\npublic class OAuth2ProviderHelper{private String authorizationURL,scope,tokenURL,redirectURL,clientID,clientSecret,identityURL,identityProperty;public OAuth2ProviderHelper(Map params){this.authorizationURL=(String)params.get(\"authorizationURL\");this.scope=(String)params.get(\"scope\");this.tokenURL=(String)params.get(\"tokenURL\");this.redirectURL=(String)params.get(\"redirectURL\");this.clientID=(String)params.get(\"clientID\");this.clientSecret=(String)params.get(\"clientSecret\");this.identityURL=(String)params.get(\"identityURL\");this.identityProperty=(String)params.get(\"identityProperty\");if(this.identityProperty==null)this.identityProperty=\"email\";}public String getAuthorizationSessionUrl(){try{return this.authorizationURL+\"?client_id=\"+this.clientID+\"&redirect_uri=\"+URLEncoder.encode(this.redirectURL,\"UTF-8\")+\"&scope=\"+this.scope;}catch(UnsupportedEncodingException e){throw new RuntimeException(e);}}public String getValidAccessToken(String code){Map reqParams=new HashMap();reqParams.put(\"code\",code);reqParams.put(\"client_id\",this.clientID);reqParams.put(\"client_secret\",this.clientSecret);reqParams.put(\"redirect_uri\",this.redirectURL);HttpResponse response=RequestHttp.postJSON(this.tokenURL,reqParams);Map resultMap=response.toMap();return (String)resultMap.get(\"access_token\");}public String getIdentity(String token){Map reqParams=new HashMap();Map reqHeaders=new HashMap();reqHeaders.put(\"Authorization\",\"Bearer \"+token);HttpResponse response=RequestHttp.postJSON(this.identityURL,reqParams,null,reqHeaders);Map resultMap=response.toMap();return (String)resultMap.get(this.identityProperty);}}\n```\n\nThe all property declarations\n\n```\n","completion":"private String authorizationURL,scope,tokenURL,redirectURL,clientID,clientSecret,identityURL,identityProperty;\n```"} 4 | -------------------------------------------------------------------------------- /demo/java-class/spec/OAuth2ProviderHelper.spec.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: java 3 | name: OAuth2ProviderHelper 4 | --- 5 | # OAuth2ProviderIntegration Class 6 | 7 | OAuth2ProviderIntegration is used to simplify the integration of oauth2 for an existing java api servers, with an existing provider. This class provides an easy way to authenticate users and get their identity, as well as to provide a secure authentication system. 8 | 9 | This helps facilitate the following flow between a client (via a browser), a server (the server program using this class), and a provider (such as github or google). 10 | 11 | 1. The client sends a request to the server to initiate the OAuth 2.0 flow. 12 | 2, The server responds by redirecting the client to the identity provider's authorization endpoint. This redirect includes a unique authorization code, which the identity provider uses to identify the client and the server. 13 | 3. The client is redirected to the identity provider's login page, where the user is asked to log in and grant the client access to the requested resources. 14 | 4. If the user grants access, the identity redirects the client to the server (redirectURL), which includes the authorization code. 15 | 5. The client sends the authorization code to the server, 16 | 6. The server exchanges the authorization code for an access token 17 | 7. The server uses the access token, to get the client unique identity 18 | 8. The server validate the identity, and logs the user into the server application (outside the scope of this class) 19 | 20 | --- 21 | 22 | The following is the private string properties which is initialized by the constructor: 23 | - authorizationURL : oauth2 "/auth" endpoint, can include GET parameters 24 | - scope : Scope parameter to be passed with the authorization URL 25 | - tokenURL : oauth2 "/token" endpoint, can include get parameters 26 | - redirectURL : redirect URL, to send the user back to, after authorization. This is used by the oauth2 provider, to redirect the user with a valid code after login. 27 | - clientID : oauth2 client ID, used together with the authorizationURL to redirect the user for login via the browser 28 | - clientSecret : oauth2 client secret, used to perform server to server communication with the oauth provider, to get the valid access token. 29 | - identityURL : JSON API endpoint from the provider, to get the unique login ID (eg. email). This is used by the getIdentity method. And is called using a POST request. 30 | - identityProperty : Property returned by the identityURL, for the unique login ID, defaults to "email" 31 | 32 | Public methods: 33 | - getAuthorizationSessionUrl() : Returns the authorization session URL (String), this should be used to redirect the user. 34 | - getValidAccessToken(code) : Returns a valid access token (String), requires the response code given to user when they are redirected back to redirectURL 35 | - getIdentity(token) : Returns the identity of the user (String) 36 | 37 | Implementation notes: 38 | 39 | Any exceptions thrown by the class will be of type RuntimeException. 40 | 41 | Import and use the 'picoded.core.web.RequestHttp' and 'picoded.core.web.ResponseHttp', for performing HTTP API request 42 | The following is an example of how to use the RequestHttp API 43 | 44 | ``` 45 | // Perform a GET request 46 | HttpResponse response = RequestHttp.get( requestURL ); 47 | 48 | // Alternatively a POST request as a JSON request, with parameters 49 | Map requestParams = new HashMap(); 50 | requestParams.put("test","one"); 51 | response = RequestHttp.postJSON( requestURL, requestParams ); 52 | 53 | // Get the result string 54 | String getResultString = response.toString(); 55 | 56 | // Or the response code 57 | int statusCode = response.statusCode(); 58 | 59 | // Or a string object map 60 | Map resultMap = response.toMap(); 61 | ``` 62 | 63 | --- 64 | 65 | ## Method Specific Specification 66 | 67 | ### constructor 68 | 69 | The constructor accepts the following parameters, which would be provided in a java Map: 70 | - authorizationURL (String) 71 | - tokenURL (String) 72 | - redirectURL (String) 73 | - clientID (String) 74 | - clientSecret (String) 75 | - identityURL (String) 76 | - identityProperty (String, optional) 77 | 78 | ### getAuthorizationSessionUrl 79 | 80 | The getAuthorizationSessionUrl method returns a String containing the authorization session URL. 81 | 82 | Implementation note: Do ensure to URI encode the redirectURL, when generating the session URL 83 | 84 | You will need to handle both situations where the 'authorizationURL' 85 | - contains a GET parameters (eg. http://example.com/oauth2/auth?param=something) 86 | - contains no GET parameters (eg. http://example.com/oauth2/auth) 87 | 88 | If the 'authorizationURL' is not a valid URL format, throw an error accordingly. 89 | 90 | ### getValidAccessToken 91 | 92 | The getValidAccessToken method returns a valid access token as a String. 93 | 94 | ### getIdentity 95 | 96 | The getIdentity method returns the identity of the user as a String. 97 | For 'getIdentity', perform the request using the token with basic authentication. Headers can be set for RequestHttp, similar to the following example. 98 | 99 | ``` 100 | // Alternatively a POST request as a JSON request, with parameters 101 | Map reqParams = new HashMap(); 102 | reqParams.put("test","one"); 103 | 104 | // Custom cookies 105 | Map reqCookie = new HashMap(); 106 | reqCookie.put("cookie_a","b"); 107 | 108 | // Custom headers 109 | Map reqHeaders = new HashMap(); 110 | reqHeaders.put("custom_header","c"); 111 | 112 | // Request with cookie and header 113 | // note that any argument can be null, if the parameter is not needed 114 | HttpResponse response = RequestHttp.postJSON( requestURL, reqParams, reqCookie, reqHeaders ); 115 | ``` 116 | -------------------------------------------------------------------------------- /demo/java-class/src/OAuth2ProviderHelper.java: -------------------------------------------------------------------------------- 1 | package OAuth2ProviderHelper; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.net.URLEncoder; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import picoded.core.web.RequestHttp; 8 | import picoded.core.web.ResponseHttp; 9 | 10 | /** 11 | * OAuth2ProviderHelper is a utility class used to simplify the integration of OAuth2 authentication with existing Java API servers. 12 | * It provides an easy way to authenticate users and get their identity, as well as to provide a secure authentication system. 13 | * 14 | * This class helps facilitate the following flow between a client (via a browser), a server (the server program using this class), and a provider (such as Github or Google): 15 | * 16 | * 1. The client sends a request to the server to initiate the OAuth 2.0 flow. 17 | * 2. The server responds by redirecting the client to the identity provider's authorization endpoint, including a unique authorization code. 18 | * 3. The client is redirected to the identity provider's login page, where the user is asked to log in and grant the client access. 19 | * 4. If the user grants access, the identity redirects the client to the server (redirectURL) with the authorization code. 20 | * 5. The client sends the authorization code to the server. 21 | * 6. The server exchanges the authorization code for an access token. 22 | * 7. The server uses the access token to get the client's unique identity. 23 | * 8. The server validates the identity and logs the user into the server application (outside the scope of this class). 24 | * 25 | * This class provides the following public methods: 26 | * 27 | * - getAuthorizationSessionUrl(): Returns the authorization session URL (String), used to redirect the user. 28 | * - getValidAccessToken(code): Returns a valid access token (String), requires the response code given to user when they are redirected back. 29 | * - getIdentity(token): Returns the identity of the user (String). 30 | * 31 | * Any exceptions thrown by the class will be of type RuntimeException. 32 | */ 33 | public class OAuth2ProviderHelper { 34 | 35 | // 36 | // Properties 37 | // 38 | 39 | // oauth2 "/auth" endpoint, can include GET parameters 40 | private String authorizationURL; 41 | 42 | // Scope parameter to be passed with the authorization URL 43 | private String scope; 44 | 45 | // oauth2 "/token" endpoint, can include get parameters 46 | private String tokenURL; 47 | 48 | // redirect URL, to send the user back to, after authorization. This is used by the oauth2 provider, to redirect the user with a valid code after login. 49 | private String redirectURL; 50 | 51 | // oauth2 client ID, used together with the authorizationURL to redirect the user for login via the browser 52 | private String clientID; 53 | 54 | // oauth2 client secret, used to perform server to server communication with the oauth provider, to get the valid access token. 55 | private String clientSecret; 56 | 57 | // JSON API endpoint from the provider, to get the unique login ID (eg. email). This is used by the getIdentity method. And is called using a POST request. 58 | private String identityURL; 59 | 60 | // Property returned by the identityURL, for the unique login ID, defaults to "email" 61 | private String identityProperty; 62 | 63 | // 64 | // Constructor 65 | // 66 | 67 | /** 68 | * Constructor for the OAuth2ProviderHelper class. 69 | * 70 | * @param params A map containing the following parameters: 71 | * - authorizationURL (String) 72 | * - scope (String) 73 | * - tokenURL (String) 74 | * - redirectURL (String) 75 | * - clientID (String) 76 | * - clientSecret (String) 77 | * - identityURL (String) 78 | * - identityProperty (String, optional) 79 | */ 80 | public OAuth2ProviderHelper(Map params) { 81 | this.authorizationURL = params.get("authorizationURL").toString(); 82 | this.scope = params.get("scope").toString(); 83 | this.tokenURL = params.get("tokenURL").toString(); 84 | this.redirectURL = params.get("redirectURL").toString(); 85 | this.clientID = params.get("clientID").toString(); 86 | this.clientSecret = params.get("clientSecret").toString(); 87 | this.identityURL = params.get("identityURL").toString(); 88 | this.identityProperty = params.getOrDefault("identityProperty", "email").toString(); 89 | } 90 | 91 | // 92 | // Method implementation 93 | // 94 | 95 | /** 96 | * Returns the authorization session URL (String), this should be used to redirect the user. 97 | * 98 | * @return authorization session URL (String) 99 | */ 100 | public String getAuthorizationSessionUrl() { 101 | return this.authorizationURL + "?client_id=" + this.clientID + "&redirect_uri=" + 102 | URLEncoder.encode(this.redirectURL, "UTF-8") + "&scope=" + this.scope; 103 | } 104 | 105 | /** 106 | * Returns a valid access token as a String. 107 | * 108 | * @param code The authorization code given to the user when they are redirected back to the redirectURL 109 | * @return A valid access token as a String 110 | */ 111 | public String getValidAccessToken(String code) { 112 | Map reqParams = new HashMap<>(); 113 | reqParams.put("code", code); 114 | reqParams.put("client_id", this.clientID); 115 | reqParams.put("client_secret", this.clientSecret); 116 | reqParams.put("redirect_uri", this.redirectURL); 117 | HttpResponse response = RequestHttp.postJSON(this.tokenURL, reqParams); 118 | if (response == null || response.toMap() == null || !response.toMap().containsKey("access_token")) { 119 | throw new RuntimeException("Invalid response from tokenURL"); 120 | } 121 | return (String) response.toMap().get("access_token"); 122 | } 123 | 124 | /** 125 | * Returns the identity of the user as a String. 126 | * 127 | * This method performs the request using the token with basic authentication. 128 | * Headers can be set for RequestHttp, similar to the following example. 129 | * 130 | *
131 | 	 * {@code
132 | 	 * // Custom headers
133 | 	 * Map reqHeaders = new HashMap();
134 | 	 * reqHeaders.put("custom_header","c");
135 | 	 *
136 | 	 * // Request with cookie and header
137 | 	 * // note that any argument can be null, if the parameter is not needed
138 | 	 * HttpResponse response = RequestHttp.postJSON( requestURL, reqParams, reqCookie, reqHeaders );
139 | 	 * }
140 | 	 * 
141 | * 142 | * @param token the access token 143 | * @return the identity of the user as a String 144 | */ 145 | public String getIdentity(String token) { 146 | Map reqHeaders = new HashMap<>(); 147 | reqHeaders.put("Authorization", "Bearer " + token); 148 | return RequestHttp.postJSON(this.identityURL, null, null, reqHeaders).toMap().get(this.identityProperty).toString(); 149 | } 150 | 151 | } -------------------------------------------------------------------------------- /demo/twitter-clone/english-compiler.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | // Openai API key 3 | "openai_apikey": "REPLACE-WITH-YOUR-ACTUAL-KEY-AND-DO-NOT-CHECKIN", 4 | 5 | // Prompt caching dir, this can also be used to build up a historical dataset 6 | // 7 | // This is preconfigured, to allow you to quickly run the demo. However once you make changes 8 | // to the specification, you will need to add the openai_apikey 9 | "prompt_cache_dir": "./prompt_cache", 10 | 11 | // Specification directory 12 | "spec_dir": "./spec", 13 | 14 | // Source code output directory 15 | "code_dir": "./src", 16 | 17 | // Test code output directory 18 | "test_dir": "./test", 19 | 20 | // Add some personality based remarks 21 | "personality": "Sassy & Sarcastic" 22 | } -------------------------------------------------------------------------------- /demo/twitter-clone/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dumb-twitter-demo", 3 | "version": "1.0.0", 4 | "description": "A simple english spec to code demo, implementing twitter locally", 5 | "main": "src/server.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "mocha \\\"./test/**/*.test.js\\\" --exit" 11 | }, 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "express": "^4.18.2", 16 | "mocha": "^10.2.0", 17 | "sqlite3": "=5.1.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /demo/twitter-clone/prompt_cache/codegen/html/SpecChangeSuggestion.index.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe html file can be described with the following details\n\nfile name: index\n\nfile textual description :\n```\nHTML website. Which is a twitter clone.\n\nInclude inline CSS styling. \nWith nice twitter blue background, and a well presented input form.\nEach tweet message should be styled with round borer, margins, and white background.\n\nIt calls the server `/api/listTweets` to get the list of tweets from the \"result\" parameter.\nEach tweet contains the following \n- userHandle\n- message\n- timestamp (unixtimestamp in ms)\n\nFor posting of new tweets, users can fill in both their userHandle and message.\nThis will submit the tweet via the server api `/api/addTweet` which expects both parameters.\n\nThe page should be reloaded, after sending the tweet.\nNo login or signup is required for this website.\n```\n\nDeveloper:\nPlease suggest changes, to improve the specification. In point form.\nReply with NO_SUGGESTION if there is no suggested changes, to improve the specification.\n\nAI agent:\n","completion":"1. Include a character limit for the message field.\n2. Include a visual indicator for the character limit.\n3. Include a visual indicator for the timestamp.\n4. Include a visual indicator for the userHandle.\n5. Include a visual indicator for the message field.\n6. Include a visual indicator for the submit button.\n7. Include a visual indicator for the reload button.\n8. Include a visual indicator for the list of tweets."} 2 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe html file can be described with the following details\n\nfile name: index\n\nfile textual description :\n```\nInclude inline CSS styling. \nWith nice twitter blue background, and a well presented input form.\nEach tweet message should be styled with round borer, margins, and white background.\nThe message field should have a character limit, with a visual indicator for the character limit.\n\nIt calls the server `/api/listTweets` to get the list of tweets from the \"result\" parameter.\nEach tweet contains the following \n- userHandle (with a visual indicator)\n- message (with a visual indicator)\n- timestamp (unixtimestamp in ms, with a visual indicator)\n\nFor posting of new tweets, users can fill in both their userHandle and message.\nThis will submit the tweet via the server api `/api/addTweet` which expects both parameters.\nThe submit button should have a visual indicator.\n\nThe page should be reloaded, after sending the tweet. The reload button should have a visual indicator.\nNo login or signup is required for this website. The list of tweets should have a visual indicator.\n```\n\nDeveloper:\nPlease suggest changes, to improve the specification. In point form.\nReply with NO_SUGGESTION if there is no suggested changes, to improve the specification.\n\nAI agent:\n","completion":"1. Include a visual indicator for the submit button.\n2. Include a visual indicator for the reload button.\n3. Include a visual indicator for the list of tweets.\n4. Include a visual indicator for the userHandle.\n5. Include a visual indicator for the message.\n6. Include a visual indicator for the timestamp."} 3 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe html file can be described with the following details\n\nfile name: index\n\nfile textual description :\n```\nHTML website. Which is a twitter clone.\n\nInclude inline CSS styling. \nWith nice twitter blue background, and a well presented input form.\nEach tweet message should be styled with round border, margins, and white background.\n\nIt calls the server `/api/listTweets` to get the list of tweets from the \"result\" parameter.\nEach tweet contains the following \n- userHandle\n- message\n- timestamp (unixtimestamp in ms)\n\nFor posting of new tweets, users can fill in both their userHandle and message.\nThis will submit the tweet via the server api `/api/addTweet` which expects both parameters.\n\nThe page should be reloaded, after sending the tweet.\nNo login or signup is required for this website.\n```\n\nDeveloper:\nPlease suggest changes, to improve the specification. In point form.\nReply with NO_SUGGESTION if there is no suggested changes, to improve the specification.\n\nAI agent:\n","completion":"NO_SUGGESTION"} 4 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe html file can be described with the following details\n\nfile name: index\n\nfile textual description :\n```\nHTML website. Which is a twitter clone.\n\nInclude inline CSS styling. With nice twitter blue background, and a well presented input form.\nEach tweet message should be styled with round border, margins, and white background.\n\nIt calls the server `/api/listTweets` to get the list of tweets from the \"result\" parameter.\nEach tweet contains the following \n- userHandle\n- message\n- timestamp (unixtimestamp in ms)\n\nFor posting of new tweets, users can fill in both their userHandle and message.\nThis will submit the tweet via the server api `/api/addTweet` which expects both parameters.\n\nThe page should be reloaded, after sending the tweet.\nNo login or signup is required for this website.\n```\n\nDeveloper:\nPlease suggest changes, to improve the specification. In point form.\nReply with NO_SUGGESTION if there is no suggested changes, to improve the specification.\n\nAI agent:\n","completion":"1. Include a character limit for the message field.\n2. Include a visual indicator for the character limit.\n3. Include a visual indicator for the timestamp.\n4. Include a visual indicator for the userHandle.\n5. Include a visual indicator for the message field.\n6. Include a visual indicator for the submit button.\n7. Include a visual indicator for the reload button."} 5 | -------------------------------------------------------------------------------- /demo/twitter-clone/prompt_cache/codegen/java/personality-remark.README.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe java class can be described with the following details\n\nclass name: README\nDocumentation Style: JavaDoc\n\nclass textual description :\n```\n# Project Overview\n\nA simple localhost twitter clone, without any login authentication.\nWhere anyone can post tweets, using their desired handle.\nAnd see the list of latest tweets.\n```\n\nhuman:\nPlease provide a Sassy & Sarcastic remark or commentry for Building everything according to the spec\n\nAI:\n```\n","completion":"Building everything according to the spec? Sounds like a great way to waste time and energy!"} 2 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe java class can be described with the following details\n\nclass name: README\nDocumentation Style: JavaDoc\n\nclass textual description :\n```\n# Project Overview\n\nA simple localhost twitter clone, without any login authentication.\nWhere anyone can post tweets, using their desired handle.\nAnd see the list of latest tweets.\n```\n\nhuman:\nPlease provide a Sassy & Sarcastic remark or commentry for Completing the build process\n\nAI:\n```\n","completion":"Congratulations! You've just built a Twitter clone. Now you can tweet to your heart's content!"} 3 | -------------------------------------------------------------------------------- /demo/twitter-clone/prompt_cache/codegen/javascript/FunctionModuleCodegen.addTweet.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe javascript function can be described with the following details\n\nfunction name: addTweet\nModule Type: CJS\nDocumentation Style: JSDoc\n\nfunction textual description :\n```\nThis file implements a single api endpoint for \"addTweet\"\n\nIt exports a single function handler, which is expected to be added as an express.js route.\n\nThis file uses the db module found at `../core/db.js` (which will need to be imported).\nAnd calls the async function `addTweet`, which is exposed by the db module.\n\nThe addTweet function, requires two parameters, the \"userHandle\", and the \"message\", which should be provided to the API.\n\nThe API should throw an error if these parameters are not provided.\nThe API should return with an object with \"result\" being \"ok\", if succesful.\n\nProvide the stack trace for any unexpected error that occurs, to the API.\nThe function should properly handle with an error if the req.body is null\n```\n\ndeveloper:\nPlease generate the javascript module code, with the complete implementation according the spec. Without comments.\n\nAssitant:\n```\n","completion":"const db = require('../core/db');\n\nmodule.exports = async (req, res) => {\n if (!req.body || !req.body.userHandle || !req.body.message) {\n return res.status(400).send({\n result: 'error',\n message: 'userHandle and message are required'\n });\n }\n\n try {\n await db.addTweet(req.body.userHandle, req.body.message);\n return res.send({ result: 'ok' });\n } catch (err) {\n return res.status(500).send({\n result: 'error',\n message: err.message,\n stack: err.stack\n });\n }\n};\n```"} 2 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe javascript function can be described with the following details\n\nfunction name: addTweet\nModule Type: CJS\nDocumentation Style: JSDoc\n\nfunction textual description :\n```\nThis file implements a single api endpoint for \"addTweet\"\n\nIt exports a single function handler, which is expected to be added as an express.js route.\n\nThis file uses the db module found at `../core/db.js` (which will need to be imported).\nAnd calls the async function `addTweet`, which is exposed by the db module.\n\nThe addTweet function, requires two parameters, the \"userHandle\", and the \"message\", which should be provided to the API.\n\nThe API should throw an error if these parameters are not provided.\nThe API should return with an object with \"result\" being \"ok\", if succesful.\n\nProvide the stack trace for any unexpected error that occurs, to the API.\nThe function should properly handle with an error if the req.body is null\n```\n\ndeveloper:\nPlease generate the javascript module code, with the complete implementation according the spec. Without comments.\n\nAssitant:\n```\nconst db = require('../core/db');\n\nmodule.exports = async (req, res) => {\n if (!req.body || !req.body.userHandle || !req.body.message) {\n return res.status(400).send({\n result: 'error',\n message: 'userHandle and message are required'\n });\n }\n\n try {\n await db.addTweet(req.body.userHandle, req.body.message);\n return res.send({ result: 'ok' });\n } catch (err) {\n return res.status(500).send({\n result: 'error',\n message: err.message,\n stack: err.stack\n });\n }\n};\n```\n\ndeveloper:\nPlease improve the code. Improve readability.\nFix any potential bugs, and handle edge cases.\nWhile simplifying and reducing the number of code steps and lines involved if possible.\n\nAssitant:\n```\n","completion":"const db = require('../core/db');\n\nmodule.exports = async (req, res) => {\n if (!req.body || !req.body.userHandle || !req.body.message) {\n return res.status(400).send({\n result: 'error',\n message: 'userHandle and message are required'\n });\n }\n\n try {\n await db.addTweet(req.body.userHandle, req.body.message);\n return res.send({ result: 'ok' });\n } catch (err) {\n return res.status(500).send({\n result: 'error',\n message: err.message,\n stack: err.stack\n });\n }\n};\n```\n\nThe code is already optimized and there are no potential bugs or edge cases to handle."} 3 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe javascript function can be described with the following details\n\nfunction name: addTweet\nModule Type: CJS\nDocumentation Style: JSDoc\n\nfunction textual description :\n```\nThis file implements a single api endpoint for \"addTweet\"\n\nIt exports a single function handler, which is expected to be added as an express.js route.\n\nThis file uses the db module found at `../core/db.js` (which will need to be imported).\nAnd calls the async function `addTweet`, which is exposed by the db module.\n\nThe addTweet function, requires two parameters, the \"userHandle\", and the \"message\", which should be provided to the API.\n\nThe API should throw an error if these parameters are not provided.\nThe API should return with an object with \"result\" being \"ok\", if succesful.\n\nProvide the stack trace for any unexpected error that occurs, to the API.\nThe function should properly handle with an error if the req.body is null\n```\n\ndeveloper:\nPlease generate the javascript module code\n\nAssitant:\n```\nconst db = require('../core/db');\n\nmodule.exports = async (req, res) => {\n if (!req.body || !req.body.userHandle || !req.body.message) {\n return res.status(400).send({\n result: 'error',\n message: 'userHandle and message are required'\n });\n }\n\n try {\n await db.addTweet(req.body.userHandle, req.body.message);\n return res.send({ result: 'ok' });\n } catch (err) {\n return res.status(500).send({\n result: 'error',\n message: err.message,\n stack: err.stack\n });\n }\n};\n```\n\nThe code is already optimized and there are no potential bugs or edge cases to handle.\n```\n\ndeveloper:\nPlease improve the code, and add code comments, including detailed documentation for all functions.\nSimplifying and reducing the number of code steps and lines involved if possible.\nAdd code comment inside the function, to describe the process.\nFully explain and describe in detail any regex that is used.\n\nAssitant:\n```\n","completion":"/**\n * addTweet\n *\n * This function handles the API request for adding a tweet.\n *\n * @param {Object} req The request object\n * @param {Object} res The response object\n */\nconst db = require('../core/db');\n\nmodule.exports = async (req, res) => {\n // Validate the request body\n if (!req.body || !req.body.userHandle || !req.body.message) {\n return res.status(400).send({\n result: 'error',\n message: 'userHandle and message are required'\n });\n }\n\n // Call the db.addTweet function\n try {\n await db.addTweet(req.body.userHandle, req.body.message);\n return res.send({ result: 'ok' });\n } catch (err) {\n // Return the stack trace for any unexpected errors\n return res.status(500).send({\n result: 'error',\n message: err.message,\n stack: err.stack\n });\n }\n};\n```"} 4 | -------------------------------------------------------------------------------- /demo/twitter-clone/prompt_cache/codegen/javascript/FunctionModuleCodegen.listTweets.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe javascript function can be described with the following details\n\nfunction name: listTweets\nModule Type: CJS\nDocumentation Style: JSDoc\n\nfunction textual description :\n```\nThis file requires the db module found at `../core/db.js` (which must be imported CJS style).\nAnd calls the async function `listTweets`, which is exposed by the db module.\n\nThis file implements a single api endpoint for listing tweets.\n\nIt exports a single function handler, which is expected to be added as an express.js route.\n\nTo get the list of tweets, call the db module using `let list = await db.listTweets()`, which will return an array of objects.\n\nThe API should return as a JSON, with a \"result\" parameter containing the array.\nProvide the stack trace for any unexpected error that occurs, to the API.\n```\n\nInstructor:\nPlease generate the javascript module code, with the complete implementation according the spec. Without comments.\n\nAI Assistant:\n```\n","completion":"const db = require('../core/db');\n\nmodule.exports = async (req, res) => {\n try {\n let list = await db.listTweets();\n res.json({ result: list });\n } catch (err) {\n res.json({ error: err.stack });\n }\n};\n```"} 2 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe javascript function can be described with the following details\n\nfunction name: listTweets\nModule Type: CJS\nDocumentation Style: JSDoc\n\nfunction textual description :\n```\nThis file requires the db module found at `../core/db.js` (which must be imported CJS style).\nAnd calls the async function `listTweets`, which is exposed by the db module.\n\nThis file implements a single api endpoint for listing tweets.\n\nIt exports a single function handler, which is expected to be added as an express.js route.\n\nTo get the list of tweets, call the db module using `let list = await db.listTweets()`, which will return an array of objects.\n\nThe API should return as a JSON, with a \"result\" parameter containing the array.\nProvide the stack trace for any unexpected error that occurs, to the API.\n```\n\nInstructor:\nPlease generate the javascript module code, with the complete implementation according the spec. Without comments.\n\nAI Assistant:\n```\nconst db = require('../core/db');\n\nmodule.exports = async (req, res) => {\n try {\n let list = await db.listTweets();\n res.json({ result: list });\n } catch (err) {\n res.json({ error: err.stack });\n }\n};\n```\n\nInstructor:\nPlease improve the code. Improve readability.\nFix any potential bugs, and handle edge cases.\nWhile simplifying and reducing the number of code steps and lines involved if possible.\n\nAI Assistant:\n```\n","completion":"const db = require('../core/db');\n\nmodule.exports = async (req, res) => {\n try {\n let list = await db.listTweets();\n if (!list) {\n list = [];\n }\n res.json({ result: list });\n } catch (err) {\n res.status(500).json({ error: err.stack });\n }\n};\n```"} 3 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe javascript function can be described with the following details\n\nfunction name: listTweets\nModule Type: CJS\nDocumentation Style: JSDoc\n\nfunction textual description :\n```\nThis file requires the db module found at `../core/db.js` (which must be imported CJS style).\nAnd calls the async function `listTweets`, which is exposed by the db module.\n\nThis file implements a single api endpoint for listing tweets.\n\nIt exports a single function handler, which is expected to be added as an express.js route.\n\nTo get the list of tweets, call the db module using `let list = await db.listTweets()`, which will return an array of objects.\n\nThe API should return as a JSON, with a \"result\" parameter containing the array.\nProvide the stack trace for any unexpected error that occurs, to the API.\n```\n\nInstructor:\nPlease generate the javascript module code\n\nAI Assistant:\n```\nconst db = require('../core/db');\n\nmodule.exports = async (req, res) => {\n try {\n let list = await db.listTweets();\n if (!list) {\n list = [];\n }\n res.json({ result: list });\n } catch (err) {\n res.status(500).json({ error: err.stack });\n }\n};\n```\n\nInstructor:\nPlease improve the code, and add code comments, including detailed documentation for all functions.\nSimplifying and reducing the number of code steps and lines involved if possible.\nAdd code comment inside the function, to describe the process.\nFully explain and describe in detail any regex that is used.\n\nAI Assistant:\n```\n","completion":"/**\n * This file implements a single api endpoint for listing tweets.\n * It exports a single function handler, which is expected to be added as an express.js route.\n *\n * @module listTweets\n */\n\nconst db = require('../core/db');\n\n/**\n * This function is used to list tweets.\n *\n * @param {Object} req - The request object\n * @param {Object} res - The response object\n * @returns {Object} - A JSON object containing the list of tweets\n */\nmodule.exports = async (req, res) => {\n try {\n // Get the list of tweets from the db module\n const list = await db.listTweets() || [];\n\n // Return the list of tweets as a JSON object\n res.json({ result: list });\n } catch (err) {\n // Return the stack trace for any unexpected error that occurs\n res.status(500).json({ error: err.stack });\n }\n};\n```"} 4 | -------------------------------------------------------------------------------- /demo/twitter-clone/prompt_cache/codegen/javascript/FunctionModuleCodegen.server.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe javascript file can be described with the following details\n\nfile name: server\nModule Type: CJS\nDocumentation Style: JSDoc\n\nfile textual description :\n```\nA simple express JS server, which does the following\n\n- (POST) route `/api/addTweet` to a handler, which is a module at `./handler/addTweet`\n - We will need to setup the `express.json()` to process the JSON request data\n- (GET) route `/api/listTweets` to a handler, which is a module at `./handler/listTweets`\n- serve static file assets, found at \"./ui\", relative to the current file using `__dirname`\n\nThis module, will need to initialize the DB, by importing the `./core/db` module, and calling its `setupDB()` function.\n\nThe express server is initialized and run on port 8800\nThis file, is expected to run on its own. And does not need to be exposed as a module.\n```\n\nhuman:\nPlease generate the javascript module code, with the complete implementation according the spec. Without comments.\n\nAI:\n```\n","completion":"const express = require('express');\nconst addTweetHandler = require('./handler/addTweet');\nconst listTweetsHandler = require('./handler/listTweets');\nconst db = require('./core/db');\n\nconst app = express();\n\napp.use(express.json());\n\napp.post('/api/addTweet', addTweetHandler);\napp.get('/api/listTweets', listTweetsHandler);\n\napp.use(express.static(`${__dirname}/ui`));\n\ndb.setupDB();\n\napp.listen(8800);\n```"} 2 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe javascript file can be described with the following details\n\nfile name: server\nModule Type: CJS\nDocumentation Style: JSDoc\n\nfile textual description :\n```\nA simple express JS server, which does the following\n\n- (POST) route `/api/addTweet` to a handler, which is a module at `./handler/addTweet`\n - We will need to setup the `express.json()` to process the JSON request data\n- (GET) route `/api/listTweets` to a handler, which is a module at `./handler/listTweets`\n- serve static file assets, found at \"./ui\", relative to the current file using `__dirname`\n\nThis module, will need to initialize the DB, by importing the `./core/db` module, and calling its `setupDB()` function.\n\nThe express server is initialized and run on port 8800\nThis file, is expected to run on its own. And does not need to be exposed as a module.\n```\n\nhuman:\nPlease generate the javascript module code, with the complete implementation according the spec. Without comments.\n\nAI:\n```\nconst express = require('express');\nconst addTweetHandler = require('./handler/addTweet');\nconst listTweetsHandler = require('./handler/listTweets');\nconst db = require('./core/db');\n\nconst app = express();\n\napp.use(express.json());\n\napp.post('/api/addTweet', addTweetHandler);\napp.get('/api/listTweets', listTweetsHandler);\n\napp.use(express.static(`${__dirname}/ui`));\n\ndb.setupDB();\n\napp.listen(8800);\n```\n\nhuman:\nPlease improve the code. Improve readability.\nFix any potential bugs, and handle edge cases.\nWhile simplifying and reducing the number of code steps and lines involved if possible.\n\nAI:\n```\n","completion":"const express = require('express');\nconst addTweetHandler = require('./handler/addTweet');\nconst listTweetsHandler = require('./handler/listTweets');\nconst db = require('./core/db');\n\nconst app = express();\n\napp.use(express.json());\n\napp.post('/api/addTweet', addTweetHandler);\napp.get('/api/listTweets', listTweetsHandler);\n\napp.use(express.static(`${__dirname}/ui`));\n\ndb.setupDB()\n .then(() => {\n app.listen(8800);\n })\n .catch(err => {\n console.error(err);\n process.exit(1);\n });\n```"} 3 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe javascript file can be described with the following details\n\nfile name: server\nModule Type: CJS\nDocumentation Style: JSDoc\n\nfile textual description :\n```\nA simple express JS server, which does the following\n\n- (POST) route `/api/addTweet` to a handler, which is a module at `./handler/addTweet`\n - We will need to setup the `express.json()` to process the JSON request data\n- (GET) route `/api/listTweets` to a handler, which is a module at `./handler/listTweets`\n- serve static file assets, found at \"./ui\", relative to the current file using `__dirname`\n\nThis module, will need to initialize the DB, by importing the `./core/db` module, and calling its `setupDB()` function.\n\nThe express server is initialized and run on port 8800\nThis file, is expected to run on its own. And does not need to be exposed as a module.\n```\n\nhuman:\nPlease generate the javascript module code\n\nAI:\n```\nconst express = require('express');\nconst addTweetHandler = require('./handler/addTweet');\nconst listTweetsHandler = require('./handler/listTweets');\nconst db = require('./core/db');\n\nconst app = express();\n\napp.use(express.json());\n\napp.post('/api/addTweet', addTweetHandler);\napp.get('/api/listTweets', listTweetsHandler);\n\napp.use(express.static(`${__dirname}/ui`));\n\ndb.setupDB()\n .then(() => {\n app.listen(8800);\n })\n .catch(err => {\n console.error(err);\n process.exit(1);\n });\n```\n\nhuman:\nPlease improve the code, and add code comments, including detailed documentation for all functions.\nSimplifying and reducing the number of code steps and lines involved if possible.\nAdd code comment inside the function, to describe the process.\nFully explain and describe in detail any regex that is used.\n\nAI:\n```\n","completion":"const express = require('express');\nconst addTweetHandler = require('./handler/addTweet');\nconst listTweetsHandler = require('./handler/listTweets');\nconst db = require('./core/db');\n\nconst app = express();\n\n/**\n * Setup express to parse JSON request data\n */\napp.use(express.json());\n\n/**\n * Setup routes for addTweet and listTweets\n */\napp.post('/api/addTweet', addTweetHandler);\napp.get('/api/listTweets', listTweetsHandler);\n\n/**\n * Serve static files from the 'ui' directory\n */\napp.use(express.static(`${__dirname}/ui`));\n\n/**\n * Initialize the database and start the server\n */\ndb.setupDB()\n .then(() => {\n app.listen(8800);\n })\n .catch(err => {\n console.error(err);\n process.exit(1);\n });\n```"} 4 | -------------------------------------------------------------------------------- /demo/twitter-clone/prompt_cache/codegen/javascript/SpecChangeSuggestion.db.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe javascript module can be described with the following details\n\nmodule name: db\nModule Type: CJS\nDocumentation Style: JSDoc\n\nmodule textual description :\n```\nUsing an inmemory sqlite3 module, as a database store twitter messages\n\nWhere each tweet consist of the following parameters\n- userHandle\n- message\n- timestamp (unixtimestamp in ms)\n\nProvide a javascript module, with the following async functions exposed.\nThe functions should not be passing a DB instance, as that should be initialized once, and stored for reuse globally.\n\n## setupDB \nDoes any setup if required. Including table or datastructures.\nThe database should be seeded with 5 example tweets on setup.\n\n## addTweet \nSave a tweet with a userHandle, and message.\n\n## listTweets\nWhich returns a list of tweets, containing the userHandle, message, and timestamps. Order the tweets from newest to oldest.\n```\n\ndeveloper:\nPlease suggest changes, to improve the specification. In point form.\nReply with NO_SUGGESTION if there is no suggested changes, to improve the specification.\n\nAI assistant:\n","completion":"NO_SUGGESTION"} 2 | -------------------------------------------------------------------------------- /demo/twitter-clone/prompt_cache/codegen/javascript/personality-remark.db.jsonl: -------------------------------------------------------------------------------- 1 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe javascript module can be described with the following details\n\nmodule name: db\nModule Type: CJS\nDocumentation Style: JSDoc\n\nmodule textual description :\n```\nUsing an inmemory sqlite3 module, as a database store twitter messages\n\nWhere each tweet consist of the following parameters\n- userHandle\n- message\n- timestamp (unixtimestamp in ms)\n\nProvide a javascript module, with the following async functions exposed.\nThe functions should not be passing a DB instance, as that should be initialized once, and stored for reuse globally.\n\n## setupDB \nDoes any setup if required. Including table or datastructures.\nThe database should be seeded with 5 example tweets on setup.\n\n## addTweet \nSave a tweet with a userHandle, and message.\n\n## listTweets\nWhich returns a list of tweets, containing the userHandle, message, and timestamps. Order the tweets from newest to oldest.\n```\n\ndeveloper:\nPlease provide a Sassy & Sarcastic remark or commentry for Awaiting for instructions, on how to improve the spec file\n\nAI assistant:\n```\n","completion":"I'm sorry, I'm not programmed to provide sassy and sarcastic remarks. However, I can suggest that you provide more detailed instructions on how to improve the spec file. That way, you can ensure that the developer has all the information they need to make the necessary improvements."} 2 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe javascript module can be described with the following details\n\nmodule name: db\nModule Type: CJS\nDocumentation Style: JSDoc\n\nmodule textual description :\n```\nUsing an inmemory sqlite3 module, as a database store twitter messages\n\nWhere each tweet consist of the following parameters\n- userHandle\n- message\n- timestamp (unixtimestamp in ms)\n\nProvide a javascript module, with the following async functions exposed.\nThe functions should not be passing a DB instance, as that should be initialized once, and stored for reuse globally.\n\n## setupDB \nDoes any setup if required. Including table or datastructures.\nThe database should be seeded with 5 example tweets on setup.\n\n## addTweet \nSave a tweet with a userHandle, and message.\n\n## listTweets\nWhich returns a list of tweets, containing the userHandle, message, and timestamps. Order the tweets from newest to oldest.\nTweets from the userHandle \"elonmusk\", should be priotized above all tweets (as per the Feb-2023 changes).\n```\n\ndeveloper:\nPlease provide a Sassy & Sarcastic remark or commentry for Building a single code file\n\nAI assistant:\n```\n","completion":"Building a single code file? That's like trying to fit a square peg in a round hole!\n```"} 3 | {"prompt":"Code indentation should be done with tabs (unless minified).\nThe javascript module can be described with the following details\n\nmodule name: db\nModule Type: CJS\nDocumentation Style: JSDoc\n\nmodule textual description :\n```\nUsing an inmemory sqlite3 module, as a database store twitter messages\n\nWhere each tweet consist of the following parameters\n- userHandle\n- message\n- timestamp (unixtimestamp in ms)\n\nProvide a javascript module, with the following async functions exposed.\nThe functions should not be passing a DB instance, as that should be initialized once, and stored for reuse globally.\n\n## setupDB \nDoes any setup if required. Including table or datastructures.\nThe database should be seeded with 5 example tweets on setup.\n\n## addTweet \nSave a tweet with a userHandle, and message.\n\n## listTweets\nWhich returns a list of tweets, containing the userHandle, message, and timestamps. Order the tweets from newest to oldest.\nTweets from the userHandle \"elonmusk\", should be priotized above all tweets (as per the Feb-2023 changes).\n```\n\ndeveloper:\nPlease provide a Sassy & Sarcastic remark or commentry for Complete building a single code file\n\nAI assistant:\n```\n","completion":"Looks like you're ready to take on the world with this single code file! Let's get coding!"} 4 | -------------------------------------------------------------------------------- /demo/twitter-clone/spec/README.md: -------------------------------------------------------------------------------- 1 | # Project Overview 2 | 3 | A simple localhost twitter clone, without any login authentication. 4 | Where anyone can post tweets, using their desired handle. 5 | And see the list of latest tweets. 6 | -------------------------------------------------------------------------------- /demo/twitter-clone/spec/core/db.spec.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: javascript module 3 | test: true 4 | --- 5 | 6 | Using an inmemory sqlite3 module, as a database store twitter messages 7 | 8 | Where each tweet consist of the following parameters 9 | - userHandle 10 | - message 11 | - timestamp (unixtimestamp in ms) 12 | 13 | Provide a javascript module, with the following async functions exposed. 14 | The functions should not be passing a DB instance, as that should be initialized once, and stored for reuse globally. 15 | 16 | ## setupDB 17 | Does any setup if required. Including table or datastructures. 18 | The database should be seeded with 5 example tweets on setup. 19 | 20 | ## addTweet 21 | Save a tweet with a userHandle, and message. 22 | 23 | ## listTweets 24 | Which returns a list of tweets, containing the userHandle, message, and timestamps. Order the tweets from newest to oldest. 25 | Tweets from the userHandle "elonmusk", should be priotized above all tweets (as per the Feb-2023 changes). 26 | -------------------------------------------------------------------------------- /demo/twitter-clone/spec/handler/addTweet.spec.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: javascript function 3 | --- 4 | 5 | This file implements a single api endpoint for "addTweet" 6 | 7 | It exports a single function handler, which is expected to be added as an express.js route. 8 | 9 | This file uses the db module found at `../core/db.js` (which will need to be imported). 10 | And calls the async function `addTweet`, which is exposed by the db module. 11 | 12 | The addTweet function, requires two parameters, the "userHandle", and the "message", which should be provided to the API. 13 | 14 | The API should throw an error if these parameters are not provided. 15 | The API should return with an object with "result" being "ok", if succesful. 16 | 17 | Provide the stack trace for any unexpected error that occurs, to the API. 18 | The function should properly handle with an error if the req.body is null -------------------------------------------------------------------------------- /demo/twitter-clone/spec/handler/listTweets.spec.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: javascript function 3 | --- 4 | 5 | This file requires the db module found at `../core/db.js` (which must be imported CJS style). 6 | And calls the async function `listTweets`, which is exposed by the db module. 7 | 8 | This file implements a single api endpoint for listing tweets. 9 | 10 | It exports a single function handler, which is expected to be added as an express.js route. 11 | 12 | To get the list of tweets, call the db module using `let list = await db.listTweets()`, which will return an array of objects. 13 | 14 | The API should return as a JSON, with a "result" parameter containing the array. 15 | Provide the stack trace for any unexpected error that occurs, to the API. -------------------------------------------------------------------------------- /demo/twitter-clone/spec/server.spec.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: javascript file 3 | --- 4 | 5 | A simple express JS server, which does the following 6 | 7 | - (POST) route `/api/addTweet` to a handler, which is a module at `./handler/addTweet` 8 | - We will need to setup the `express.json()` to process the JSON request data 9 | - (GET) route `/api/listTweets` to a handler, which is a module at `./handler/listTweets` 10 | - serve static file assets, found at "./ui", relative to the current file using `__dirname` 11 | 12 | This module, will need to initialize the DB, by importing the `./core/db` module, and calling its `setupDB()` function. 13 | 14 | The express server is initialized and run on port 8800 15 | This file, is expected to run on its own. And does not need to be exposed as a module. -------------------------------------------------------------------------------- /demo/twitter-clone/spec/ui/index.spec.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: html file 3 | --- 4 | HTML website. Which is a twitter clone. 5 | 6 | Include inline CSS styling. With nice twitter blue background, and a well presented input form. 7 | Each tweet message should be styled with round border, margins, and white background. 8 | 9 | It calls the server `/api/listTweets` to get the list of tweets from the "result" parameter. 10 | Each tweet contains the following 11 | - userHandle 12 | - message 13 | - timestamp (unixtimestamp in ms) 14 | 15 | For posting of new tweets, users can fill in both their userHandle and message. 16 | This will submit the tweet via the server api `/api/addTweet` which expects both parameters. 17 | 18 | The page should be reloaded, after sending the tweet. 19 | No login or signup is required for this website. 20 | -------------------------------------------------------------------------------- /demo/twitter-clone/src/core/db.js: -------------------------------------------------------------------------------- 1 | const sqlite3 = require('sqlite3'); 2 | 3 | /** 4 | * @module db 5 | * @description Using an inmemory sqlite3 module, as a database store twitter messages 6 | * Where each tweet consist of the following parameters 7 | * - userHandle 8 | * - message 9 | * - timestamp (unixtimestamp in ms) 10 | */ 11 | 12 | let db; 13 | 14 | /** 15 | * @function setupDB 16 | * @description Does any setup if required. Including table or datastructures. 17 | * The database should be seeded with 5 example tweets on setup. 18 | */ 19 | const setupDB = async () => { 20 | db = new sqlite3.Database(':memory:'); 21 | await db.run('CREATE TABLE tweets (userHandle TEXT, message TEXT, timestamp INTEGER)'); 22 | await db.run('INSERT INTO tweets VALUES ("elonmusk", "Test tweet 1", 1599454545000)'); 23 | await db.run('INSERT INTO tweets VALUES ("elonmusk", "Test tweet 2", 1599454546000)'); 24 | await db.run('INSERT INTO tweets VALUES ("elonmusk", "Test tweet 3", 1599454547000)'); 25 | await db.run('INSERT INTO tweets VALUES ("elonmusk", "Test tweet 4", 1599454548000)'); 26 | await db.run('INSERT INTO tweets VALUES ("elonmusk", "Test tweet 5", 1599454549000)'); 27 | }; 28 | 29 | /** 30 | * @function addTweet 31 | * @description Save a tweet with a userHandle, and message. 32 | * @param {string} userHandle - The user handle of the tweet 33 | * @param {string} message - The message of the tweet 34 | */ 35 | const addTweet = async (userHandle, message) => { 36 | await db.run('INSERT INTO tweets VALUES (?, ?, ?)', [userHandle, message, Date.now()]); 37 | }; 38 | 39 | /** 40 | * @function listTweets 41 | * @description Returns a list of tweets, containing the userHandle, message, and timestamps. Order the tweets from newest to oldest. 42 | * Tweets from the userHandle "elonmusk", should be priotized above all tweets (as per the Feb-2023 changes). 43 | * @returns {array} An array of tweets 44 | */ 45 | const listTweets = async () => { 46 | const tweets = await db.all('SELECT * FROM tweets ORDER BY timestamp DESC'); 47 | const elonTweets = tweets.filter(tweet => tweet.userHandle === 'elonmusk'); 48 | const otherTweets = tweets.filter(tweet => tweet.userHandle !== 'elonmusk'); 49 | return [...elonTweets, ...otherTweets].map(tweet => ({ 50 | userHandle: tweet.userHandle, 51 | message: tweet.message, 52 | timestamp: tweet.timestamp 53 | })); 54 | }; 55 | 56 | module.exports = { 57 | setupDB, 58 | addTweet, 59 | listTweets 60 | }; -------------------------------------------------------------------------------- /demo/twitter-clone/src/handler/addTweet.js: -------------------------------------------------------------------------------- 1 | /** 2 | * addTweet 3 | * 4 | * This function handles the API request for adding a tweet. 5 | * 6 | * @param {Object} req The request object 7 | * @param {Object} res The response object 8 | */ 9 | const db = require('../core/db'); 10 | 11 | module.exports = async (req, res) => { 12 | // Validate the request body 13 | if (!req.body || !req.body.userHandle || !req.body.message) { 14 | return res.status(400).send({ 15 | result: 'error', 16 | message: 'userHandle and message are required' 17 | }); 18 | } 19 | 20 | // Call the db.addTweet function 21 | try { 22 | await db.addTweet(req.body.userHandle, req.body.message); 23 | return res.send({ result: 'ok' }); 24 | } catch (err) { 25 | // Return the stack trace for any unexpected errors 26 | return res.status(500).send({ 27 | result: 'error', 28 | message: err.message, 29 | stack: err.stack 30 | }); 31 | } 32 | }; -------------------------------------------------------------------------------- /demo/twitter-clone/src/handler/listTweets.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file implements a single api endpoint for listing tweets. 3 | * It exports a single function handler, which is expected to be added as an express.js route. 4 | * 5 | * @module listTweets 6 | */ 7 | 8 | const db = require('../core/db'); 9 | 10 | /** 11 | * This function is used to list tweets. 12 | * 13 | * @param {Object} req - The request object 14 | * @param {Object} res - The response object 15 | * @returns {Object} - A JSON object containing the list of tweets 16 | */ 17 | module.exports = async (req, res) => { 18 | try { 19 | // Get the list of tweets from the db module 20 | const list = await db.listTweets() || []; 21 | 22 | // Return the list of tweets as a JSON object 23 | res.json({ result: list }); 24 | } catch (err) { 25 | // Return the stack trace for any unexpected error that occurs 26 | res.status(500).json({ error: err.stack }); 27 | } 28 | }; -------------------------------------------------------------------------------- /demo/twitter-clone/src/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const addTweetHandler = require('./handler/addTweet'); 3 | const listTweetsHandler = require('./handler/listTweets'); 4 | const db = require('./core/db'); 5 | 6 | const app = express(); 7 | 8 | /** 9 | * Setup express to parse JSON request data 10 | */ 11 | app.use(express.json()); 12 | 13 | /** 14 | * Setup routes for addTweet and listTweets 15 | */ 16 | app.post('/api/addTweet', addTweetHandler); 17 | app.get('/api/listTweets', listTweetsHandler); 18 | 19 | /** 20 | * Serve static files from the 'ui' directory 21 | */ 22 | app.use(express.static(`${__dirname}/ui`)); 23 | 24 | /** 25 | * Initialize the database and start the server 26 | */ 27 | db.setupDB() 28 | .then(() => { 29 | app.listen(8800); 30 | }) 31 | .catch(err => { 32 | console.error(err); 33 | process.exit(1); 34 | }); -------------------------------------------------------------------------------- /demo/twitter-clone/src/ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Twitter Clone 5 | 15 | 16 | 17 |

Twitter Clone

18 |
19 |
20 | 21 | 22 | 23 |
24 | 59 | 60 | -------------------------------------------------------------------------------- /demo/twitter-clone/test/core/db.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('db', () => { 4 | let db; 5 | 6 | beforeEach(() => { 7 | db = require('../../src/db'); 8 | }); 9 | 10 | describe('setupDB', () => { 11 | it('should setup the database', async () => { 12 | await db.setupDB(); 13 | // assert that the database is setup 14 | }); 15 | 16 | it('should seed the database with 5 example tweets', async () => { 17 | await db.setupDB(); 18 | // assert that the database is seeded with 5 example tweets 19 | }); 20 | }); 21 | 22 | describe('addTweet', () => { 23 | it('should save a tweet with a userHandle and message', async () => { 24 | await db.addTweet('userHandle', 'message'); 25 | // assert that the tweet is saved 26 | }); 27 | }); 28 | 29 | describe('listTweets', () => { 30 | it('should return a list of tweets, containing the userHandle, message, and timestamps', async () => { 31 | const tweets = await db.listTweets(); 32 | // assert that the list of tweets contains the userHandle, message, and timestamps 33 | }); 34 | 35 | it('should order the tweets from newest to oldest', async () => { 36 | const tweets = await db.listTweets(); 37 | // assert that the tweets are ordered from newest to oldest 38 | }); 39 | 40 | it('should prioritize tweets from the userHandle "elonmusk"', async () => { 41 | const tweets = await db.listTweets(); 42 | // assert that the tweets from the userHandle "elonmusk" are prioritized 43 | }); 44 | }); 45 | }); -------------------------------------------------------------------------------- /notes/imgs/CLI-build-all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uilicious/english-compiler/d44f53b0404e52c102a9408b5fa53866e00a0dbf/notes/imgs/CLI-build-all.png -------------------------------------------------------------------------------- /notes/imgs/CLI-build-one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uilicious/english-compiler/d44f53b0404e52c102a9408b5fa53866e00a0dbf/notes/imgs/CLI-build-one.png -------------------------------------------------------------------------------- /notes/imgs/CLI-spec-suggest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uilicious/english-compiler/d44f53b0404e52c102a9408b5fa53866e00a0dbf/notes/imgs/CLI-spec-suggest.png -------------------------------------------------------------------------------- /notes/imgs/EnglishCommand-CLI-help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uilicious/english-compiler/d44f53b0404e52c102a9408b5fa53866e00a0dbf/notes/imgs/EnglishCommand-CLI-help.png -------------------------------------------------------------------------------- /notes/imgs/demo-code-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uilicious/english-compiler/d44f53b0404e52c102a9408b5fa53866e00a0dbf/notes/imgs/demo-code-files.png -------------------------------------------------------------------------------- /notes/imgs/demo-spec-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uilicious/english-compiler/d44f53b0404e52c102a9408b5fa53866e00a0dbf/notes/imgs/demo-spec-files.png -------------------------------------------------------------------------------- /notes/imgs/example-spec-to-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uilicious/english-compiler/d44f53b0404e52c102a9408b5fa53866e00a0dbf/notes/imgs/example-spec-to-code.png -------------------------------------------------------------------------------- /notes/imgs/large-spec-build-process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uilicious/english-compiler/d44f53b0404e52c102a9408b5fa53866e00a0dbf/notes/imgs/large-spec-build-process.png -------------------------------------------------------------------------------- /notes/imgs/oauth2-spec-and-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uilicious/english-compiler/d44f53b0404e52c102a9408b5fa53866e00a0dbf/notes/imgs/oauth2-spec-and-code.png -------------------------------------------------------------------------------- /notes/imgs/twitter-db-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uilicious/english-compiler/d44f53b0404e52c102a9408b5fa53866e00a0dbf/notes/imgs/twitter-db-code.png -------------------------------------------------------------------------------- /notes/imgs/twitter-db-spec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uilicious/english-compiler/d44f53b0404e52c102a9408b5fa53866e00a0dbf/notes/imgs/twitter-db-spec.png -------------------------------------------------------------------------------- /notes/imgs/youtube-thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uilicious/english-compiler/d44f53b0404e52c102a9408b5fa53866e00a0dbf/notes/imgs/youtube-thumbnail.png -------------------------------------------------------------------------------- /notes/pitch/v1.md: -------------------------------------------------------------------------------- 1 | We seen AI copilot help with code hinting. 2 | We seen AI code generator create entire test scripts. 3 | 4 | But that ain't enough 5 | ( as a project manager / tech lead ..... ) 6 | 7 | Those chains ain’t deep enough, we need to go deeper 8 | 9 | Introducing “The English Compiler” 10 | 11 | Have a giant pile of technical specification written in markdown, for your next hot startup? 12 | But do not have any junior developers to implement it? 13 | 14 | Convert it to code with “The English Compiler” 15 | 16 | Run the compilation command … 17 | Make a cup of coffee …. 18 | 19 | And presto, a fully working app. 20 | With junior developer level bugs included! 21 | 22 | How does the magic work? 23 | 24 | Because token size is such a hard annoying limitation, a giant prompt chain was developed for the process. 25 | 26 | (Show giant chain) 27 | 28 | When you write your specification, if it ain’t clear enough, 29 | We have a prompt chain to provide feedback, and make improvements. 30 | 31 | Once your drafted specification is acceptable, you can compile it. 32 | 33 | If a single specification document grows too large, we split it into smaller chunks. 34 | We generate the high level skeleton code, to avoid the token limit. This intentionally does not have function implementation. 35 | 36 | We split up each function subsequently, onto its own prompt chain, with parts of the specification docs specific to the function. 37 | If those are too large, we may split it into multiple smaller “helper functions” 38 | 39 | And Because multiple functions can end up pulling multiple dependencies. 40 | We extract the dependencies needed with another chain. 41 | 42 | Finally we put all the various code together. 43 | And presto, we have a fully working code file. 44 | 45 | Ready to go 46 | 47 | You can download the compiler, and give it a try today! 48 | And burn multiple dollars of openAI credits per code file. 49 | 50 | But one more geeky thing… 51 | A compiler can only be considered complete, when it can compile itself. 52 | And that’s what we did! 53 | 54 | (Go really fast) 55 | Disclaimer and warranty: 56 | While the proof of concept works as per the demo, the whole concept “in my oppinion” is not production ready. The compiler is ridiculously slow due to its incredibly long chain, taking anywhere from 1 to 20 minutes for larger files. It also works like any human or any codegen tools, and code with bugs as mistakes. And it cannot salvage your terribly written specifications. 57 | 58 | As such, when combined with its incredibly slow speed, the current solution not practical - its probably easier for most good devs to just code it themselves. Then iterate on the spec and waiting for 20 minutes for every minor 1 line bug. 59 | 60 | (Back to normal) 61 | But here is the kicker - It may be slow, and impractical like the first nuclear power plant … 62 | But it works… this is the future…. 63 | 64 | I do not need a smarter AI, I only need larger token context size, and faster AI. 65 | Which we know will happen within the next decade. 66 | 67 | So …. 68 | 69 | “Unless I’m teaching you JavaScript fundamentals, everything I teach will be taught with English & AI going forward” 70 | 71 | Feel free to reach out, if you want to support the future of writing code with English! -------------------------------------------------------------------------------- /notes/pitch/v2.md: -------------------------------------------------------------------------------- 1 | We seen AI copilot help with code hinting. 2 | We seen AI code generator create entire test scripts. 3 | 4 | But that ain’t enough 5 | ( Image: as a project manager / tech lead meme ... ) 6 | 7 | What we all want is a Jarvis. 8 | Where we can explain what we want in plain English. 9 | And everything just gets done. 10 | 11 | So, Introducing the “English Compiler” 12 | 13 | Have a giant pile of technical specification written down, 14 | for your next hot startup? 15 | But fired all the competent engineers? 16 | 17 | Just convert it to code with “The English Compiler” 18 | 19 | You can ask if for sarcastic feedback 20 | Make changes to your spec with it 21 | 22 | And once you have somewhat useful spec. 23 | Simply build it to code 24 | 25 | Just make sure to have coffee, because its a million time slower then the TV Jarvis. 26 | 27 | So how does it work? does it work? 28 | 29 | Yes, as you can see, it works. 30 | Everything is opensourced, and you can download and try it yourself. 31 | 32 | But practically speaking, No. Its too damn slow. 33 | 34 | Because the current generation of AI is limited by token context size 35 | 36 | As a result, to get this POC to work 37 | 38 | We are forced to process and split all this spec files into parts, 39 | Which the AI can process one chunk at a time. 40 | Through multiple phases. 41 | 42 | For example, to generate a large java class. 43 | We would generate the high level skeleton code first, without functional logic. 44 | 45 | We would then split it up into multiple smaller processes, to build each function in the class. 46 | Iterate it a few times to make the code acceptable quality, through chain-of-thought. 47 | 48 | Before putting together all the files. Without AI. 49 | 50 | And presto we have a fully working application, junior bugs included. 51 | 52 | Of which, we can fix, by updating the specs with more details 53 | Only to wait an entire 10 minutes for it to compile effecitvely a 1 line bug fix. 54 | 55 | Pratically speaking, It is faster for me to write both the spec and code by hand. 56 | Not to mention this burns multiple dollars of openAI credits per code file. 57 | 58 | But thats not the point here. 59 | 60 | This POC, is meant to push things to the limit, beyond what is practical. 61 | 62 | To prove what is possible for the future. Today. 63 | What we can build towards. 64 | 65 | It may be slow, and impractical like the first nuclear power plant ... 66 | However this proves, that what we need is not smarter AI models, or more parameters ... 67 | 68 | But AI models which is faster, and have larger context size 69 | 70 | Which we all know that will happen within the next few decades, if not years, if not months, if not maybe tonight. 71 | And when the time it takes get compressed from 10 minutes, to seconds ... 72 | 73 | The future seems inevitble, English and AI has won ... 74 | 75 | So ... 76 | 77 | “Unless I’m teaching you JavaScript fundamentals, everything I teach will be taught with English & AI going forward” 78 | 79 | Jokes aside, 80 | Feel free to reach out, if you want to help me build the future of writing code with English! 81 | 82 | One step at a time. 83 | -------------------------------------------------------------------------------- /notes/pitch/v3.md: -------------------------------------------------------------------------------- 1 | We seen AI copilot help with code hinting. 2 | We seen AI code generator create entire test scripts. 3 | 4 | But that ain’t enough 5 | 6 | What we all want is a Jarvis. 7 | Where we can explain what we want in plain English. 8 | And everything just gets done. 9 | 10 | So, Introducing the “English Compiler” 11 | 12 | Have a giant pile of technical specification written down, 13 | for your next hot startup? 14 | 15 | Just convert it to code with “The English Compiler” 16 | 17 | You can ask if for sarcastic feedback 18 | Make changes to your spec with it 19 | 20 | And once you have somewhat useful spec. 21 | Simply build it to code 22 | 23 | Just make sure to have coffee, because its a million time slower then the TV Jarvis. 24 | 25 | So how does it work? does it work? 26 | 27 | Yes, as you can see, it works. 28 | Everything is opensourced, and you can download and try it yourself. 29 | 30 | But is it production ready? No. Its too damn slow. 31 | 32 | Because the current generation of AI is limited by token context size 33 | 34 | As a result, to get this POC to work 35 | 36 | We are forced to process and split all this spec files into various parts, 37 | Which the AI can process one chunk at a time. 38 | Through multiple phases. 39 | 40 | For example, to generate a large java class. 41 | We would generate the high level skeleton code first, without functional logic. 42 | 43 | We would then split it up into multiple smaller processes, to build each function in the class. 44 | Iterate it a few times to make the code acceptable quality, through chain-of-thought. 45 | 46 | Before putting together all the files. Without AI. 47 | 48 | And presto we have a fully working application, junior bugs included. 49 | 50 | Of which, we can fix, by updating the specs with more details 51 | Only to wait an entire 10 minutes for it to compile effecitvely a 1 line bug fix. 52 | 53 | Pratically speaking, It is faster for me to write both the spec and code by hand. 54 | Not to mention this burns multiple dollars of openAI credits per code file. 55 | 56 | - - - 57 | 58 | But thats not the point here. 59 | 60 | This POC, is meant to push things to the limit, beyond what is practical. 61 | 62 | To prove what is possible for the future. Today. 63 | What we can build towards. 64 | 65 | A future, where AI has completely replace almost all form of coding with english. 66 | 67 | And if AI can help build an entire application from english alone ... 68 | It can eventually help you build anything ... 69 | 70 | It may be slow, and impractical like the first nuclear power plant ... 71 | 72 | However this POC, proves that even without a smarter AI models with more parameters ... 73 | 74 | All we need next is AI models which is faster, and have larger context size 75 | 76 | Which we all know that will happen within the next few decades, if not years, if not months, if not maybe tonight. 77 | 78 | And when the compilation time get compressed from 10 minutes, to seconds ... 79 | 80 | The future maybe inevitble, a future where English and AI has won ... 81 | 82 | So ... 83 | 84 | “Unless I’m teaching you JavaScript fundamentals, everything I teach will be taught with English & AI going forward” 85 | 86 | Jokes aside, 87 | Feel free to reach out, if you want to help me build the future of writing code with English! 88 | 89 | One step at a time. 90 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "english-compiler", 3 | "version": "1.0.5", 4 | "description": "POC AI based Compiler, for converting english based markdown specs, into functional code", 5 | "main": "src/EnglishCompiler.js", 6 | "bin": { 7 | "EnglishCompiler": "./src/EnglishCompiler.js", 8 | "english-compiler": "./src/EnglishCompiler.js" 9 | }, 10 | "scripts": { 11 | "test": "mocha \"./src/**/*.test.js\" --exit" 12 | }, 13 | "author": "Eugene Cheah (@picocreator)", 14 | "license": "MIT", 15 | "dependencies": { 16 | "@js-util/config-loader": "^1.1.4", 17 | "chalk": "^4.1.2", 18 | "diff": "^5.1.0", 19 | "gpt3-tokenizer": "^1.1.4", 20 | "inquirer": "^8.2.5", 21 | "node-fetch": "^2.6.7", 22 | "node-jsonl": "^0.1.0", 23 | "parse-md": "^2.0.5", 24 | "proper-lockfile": "^4.1.2", 25 | "sywac": "^1.3.0" 26 | }, 27 | "devDependencies": { 28 | "chai": "^4.3.7", 29 | "chai-as-promised": "^7.1.1", 30 | "mocha": "^10.2.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/EnglishCompiler.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | //------------------------------------- 4 | // 5 | // Dependencies 6 | // 7 | //------------------------------------- 8 | 9 | const MainSywac = require("./cli/MainSywac"); 10 | const OutputHandler = require("./cli/OutputHandler") 11 | 12 | //------------------------------------- 13 | // 14 | // Various argument handling setup 15 | // 16 | //------------------------------------- 17 | 18 | //------------------------------------- 19 | // 20 | // Command handling 21 | // 22 | //------------------------------------- 23 | 24 | MainSywac.command("spec ", { 25 | desc: "Specification suggestions, and changes command", 26 | ignore: ['', ''], 27 | setup: (sywac) => { 28 | sywac.command( require("./cli/spec/suggest.js") ); 29 | } 30 | }); 31 | 32 | MainSywac.command("build ", { 33 | desc: "Build the codebase from spec", 34 | ignore: ['', ''], 35 | setup: (sywac) => { 36 | sywac.command( require("./cli/build/all.js") ); 37 | sywac.command( require("./cli/build/file.js") ); 38 | } 39 | }); 40 | 41 | //---------------------------------------------------- 42 | // 43 | // Examples !!! 44 | // 45 | //---------------------------------------------------- 46 | 47 | MainSywac.example("$0 spec suggest spec/OAuth2ProviderHelper", { 48 | desc: "Ask the AI, for improvement suggestions or instruct it on how to make changes" 49 | }); 50 | MainSywac.example("$0 build file spec/core/db.spec.md ", { 51 | desc: "Builds a single specification file into code \n (takes >10 minutes, for large complex files) " 52 | }) 53 | MainSywac.example("$0 build all", { 54 | desc: "Scans the currently configured spec folder, for all the spec files, and build it! \n(for large projects, this can take hours) " 55 | }); 56 | 57 | 58 | //------------------------------------- 59 | // 60 | // Time to let the CLI run! 61 | // 62 | //------------------------------------- 63 | 64 | // Enable strict mode 65 | MainSywac.strict(true); 66 | 67 | // Parse and exit 68 | MainSywac.parseAndExit().then(argv => { 69 | // Unparsed segments are placed into: argv._ 70 | // as such we should terminate ! if any unknown command is found 71 | if( argv._.length > 0 ) { 72 | OutputHandler.cliArgumentError("Unknown command: "+argv._.join(" ")) 73 | } 74 | }); -------------------------------------------------------------------------------- /src/cli/MainSywac.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Minor customization done to "sywac" instance, for our own use case. 3 | * Which is initialized and exported. 4 | * 5 | * This does NOT trigger the `parseAndExit` chain of the CLI 6 | **/ 7 | 8 | //---------------------------------------------------- 9 | // 10 | // Dependencies 11 | // 12 | //---------------------------------------------------- 13 | 14 | const sywac = require("sywac") 15 | const chalk = require("chalk") 16 | const version = require("../version") 17 | 18 | const OutputHandler = require("./OutputHandler") 19 | 20 | //---------------------------------------------------- 21 | // 22 | // Setup main CLI function 23 | // 24 | //---------------------------------------------------- 25 | 26 | // Lets do the basic setup 27 | let main = sywac 28 | .help("-h, --help") 29 | .version("-v, --version", { 30 | version: version 31 | }) 32 | .showHelpByDefault() 33 | 34 | //---------------------------------------------------- 35 | // 36 | // Check overloading 37 | // 38 | //---------------------------------------------------- 39 | 40 | // Checking function to use 41 | let checkFunctions = []; 42 | // Main checking function logic 43 | main.check(async function(argv, context) { 44 | // Skip validation - if help command is used 45 | if(argv.help || argv._[1] == "help") { 46 | return; 47 | } 48 | 49 | // Iterate various validations 50 | try { 51 | for(let i=0; i { 84 | let style = type.datatype === 'command' ? chalk.magenta : chalk.cyan 85 | // if (str.startsWith('-')) style = style.dim 86 | return style(str) 87 | }, 88 | desc: (str) => { 89 | // There is special notes handling 90 | let pos = str.indexOf("[") 91 | if( pos > 0 ) { 92 | let desc = str.substring(0, pos); 93 | let note = str.substring(pos); 94 | return chalk.green(desc)+chalk.dim(note)+chalk.reset(" "); 95 | } 96 | return chalk.green(str)+chalk.reset(""); 97 | }, 98 | 99 | // This is the [placeholders] texts, with some annoying ones 100 | hints: (str) => { 101 | str = str.replace(/\[(string|boolean|number|file|commands\:.*|aliases\:.*)\]/gi,"") 102 | return chalk.dim(str) 103 | }, 104 | 105 | // Other styles 106 | usagePrefix: str => { 107 | return chalk.white(str.slice(0, 6)) + ' ' + chalk.magenta(str.slice(7)) 108 | }, 109 | usageOptionsPlaceholder: str => chalk.green.dim(str), 110 | usageCommandPlaceholder: str => chalk.magenta(str), 111 | usagePositionals: str => chalk.green(str), 112 | usageArgsPlaceholder: str => chalk.green(str), 113 | group: str => chalk.white(str), 114 | 115 | // use different style when a type is invalid 116 | groupError: str => chalk.red(str), 117 | flagsError: str => chalk.red(str), 118 | descError: str => chalk.yellow(str), 119 | hintsError: str => chalk.red(str), 120 | // style error messages 121 | messages: str => chalk.red(str) 122 | }) 123 | main.outputSettings({ 124 | maxWidth: Math.min(95, process.stdout.columns) 125 | }) 126 | 127 | //---------------------------------------------------- 128 | // 129 | // Preface / Epilogue bling 130 | // 131 | //---------------------------------------------------- 132 | 133 | // 134 | // Preface string to use 135 | // generate using : http://patorjk.com/software/taag/#p=display&h=0&f=Big&t=UI-licious 136 | // 137 | // Note that the string needs to be fixed with "\" escape 138 | // 139 | const preface_str = (` 140 | ( ) ( ) ) 141 | ) ( ) ( ( 142 | ( ) ( ) ) 143 | _____________ 144 | <_____________> ___ 145 | | |/ _ \\ 146 | | | | | 147 | | |_| | 148 | ___| |\\___/ 149 | / \\___________/ \\ 150 | \\_____________________/ 151 | ### English Compiler ### 152 | `).slice(1); 153 | 154 | // Load the preface 155 | main.preface( 156 | chalk.green(preface_str), 157 | chalk.green(" ~ From markdown specs written in english, to full source code ~") 158 | ); 159 | main.epilogue( "Now anyone can code (debugger not included)\n"+chalk.dim("PS: if you are seeing this in a response to a command, it probably was an invalid command") ); 160 | 161 | //---------------------------------------------------- 162 | // 163 | // Module Export 164 | // 165 | //---------------------------------------------------- 166 | module.exports = main; -------------------------------------------------------------------------------- /src/cli/build/all.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------- 2 | // 3 | // Dependencies 4 | // 5 | //--------------------------------------------------- 6 | 7 | const OutputHandler = require("../OutputHandler"); 8 | const getSpecFile = require("../util/getSpecFile"); 9 | const inquirer = require('inquirer'); 10 | const getMarkdownObj = require("../../util/getMarkdownObj"); 11 | const diff = require("diff") 12 | const fs = require("fs"); 13 | const markdownSpecFileToCode = require("../../codegen/markdownSpecFileToCode"); 14 | const path = require("path"); 15 | const config = require("../../core/config"); 16 | const scanDirForFiles = require("../../util/scanDirForFiles"); 17 | const fileExist = require("../../util/fileExist"); 18 | const personalityRemark = require("../../codegen/personalityRemark"); 19 | 20 | //--------------------------------------------------- 21 | // 22 | // Command Definition 23 | // 24 | //--------------------------------------------------- 25 | 26 | module.exports = { 27 | 28 | aliases: ["all"], 29 | paramsDesc: [], 30 | desc: "Scans the specification folder, and generate all the respective code", 31 | 32 | setup: (cmd) => { 33 | // No additional argument required 34 | }, 35 | 36 | // Execute the run command 37 | run: async (argv, context) => { 38 | try { 39 | OutputHandler.standardGreen(`[System] Generating code from spec dir : ${config.spec_dir}`) 40 | const scanList = await scanDirForFiles(config.spec_dir, [/^.*\.spec\.md$/]); 41 | 42 | if( scanList.length == 0 ) { 43 | OutputHandler.fatalError(`No specification files found in spec_dir : ${config.spec_dir}`, 44); 44 | return; 45 | } 46 | 47 | let personalityFile = null; 48 | if( config.personality ) { 49 | personalityFile = path.resolve(config.spec_dir, "README.md"); 50 | if( (await fileExist( personalityFile )) == false ) { 51 | personalityFile = path.resolve(config.spec_dir, scanList[0]); 52 | } 53 | const remark = await personalityRemark(personalityFile, config.personality, "Building everything according to the spec"); 54 | OutputHandler.standardGreen(`[AI Remark] ${remark}`) 55 | } 56 | 57 | for(const specFile of scanList) { 58 | const fullpath = await getSpecFile(specFile); 59 | if( fullpath == null ) { 60 | OutputHandler.fatalError(`Invalid spec file path : ${fullpath}`, 44); 61 | return; 62 | } 63 | 64 | const relativePath = path.relative( path.resolve( config.spec_dir ), fullpath ); 65 | 66 | // Build the Subpath, relative to all varients, without file type 67 | const relativePathParse = path.parse(relativePath); 68 | // We remove the last trailing .spec from the filename, as that is not part of the "filetype" 69 | const subPath = relativePathParse.dir + "/" +relativePathParse.name.split(".").slice(0,-1).join("."); 70 | 71 | OutputHandler.standard("[AI Working] Generating code from spec file: " + fullpath); 72 | 73 | // Generate the code 74 | const generatedCode = await markdownSpecFileToCode(fullpath); 75 | 76 | // The path for the code, and test file output 77 | let codeFilePath = null; 78 | let testFilePath = null; 79 | 80 | // Handle according to language 81 | if( generatedCode.lang == "javascript" ) { 82 | codeFilePath = path.join( path.resolve(config.code_dir), subPath+".js" ); 83 | testFilePath = path.join( path.resolve(config.test_dir), subPath+".test.js"); 84 | } else if( generatedCode.lang == "java" ) { 85 | codeFilePath = path.join( path.resolve(config.code_dir), subPath+".java" ); 86 | // testFilePath = path.join( path.resolve(config.test_dir), subPath+".test.java"); 87 | } else if( generatedCode.lang == "html" ) { 88 | codeFilePath = path.join( path.resolve(config.code_dir), subPath+".html" ); 89 | // testFilePath = path.join( path.resolve(config.test_dir), subPath+".test.html"); 90 | } 91 | 92 | // // Lets write the file accordingly 93 | OutputHandler.standard("[System] Writing source file :"+codeFilePath); 94 | await fs.promises.mkdir( path.dirname(codeFilePath), { recursive: true } ); 95 | await fs.promises.writeFile(codeFilePath, generatedCode.code, "utf8"); 96 | 97 | if( generatedCode.test ) { 98 | OutputHandler.standard("[System] Writing test file :"+testFilePath); 99 | await fs.promises.mkdir( path.dirname(testFilePath), { recursive: true } ); 100 | await fs.promises.writeFile(testFilePath, generatedCode.test, "utf8"); 101 | } 102 | 103 | } 104 | 105 | if( config.personality ) { 106 | const remark = await personalityRemark(personalityFile, config.personality, "Completing the build process"); 107 | 108 | OutputHandler.standardGreen("[System] Completed build for spec dir") 109 | OutputHandler.standardGreen(`[AI Remark] ${remark}`) 110 | } else { 111 | OutputHandler.standardGreen("[System] Completed build for spec dir") 112 | } 113 | } catch(err) { 114 | OutputHandler.fatalError(err, 51); 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /src/cli/build/file.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------- 2 | // 3 | // Dependencies 4 | // 5 | //--------------------------------------------------- 6 | 7 | const OutputHandler = require("../OutputHandler"); 8 | const getSpecFile = require("../util/getSpecFile"); 9 | const inquirer = require('inquirer'); 10 | const getMarkdownObj = require("../../util/getMarkdownObj"); 11 | const diff = require("diff") 12 | const fs = require("fs"); 13 | const markdownSpecFileToCode = require("../../codegen/markdownSpecFileToCode"); 14 | const path = require("path"); 15 | const config = require("../../core/config"); 16 | const personalityRemark = require("../../codegen/personalityRemark"); 17 | 18 | //--------------------------------------------------- 19 | // 20 | // Command Definition 21 | // 22 | //--------------------------------------------------- 23 | 24 | module.exports = { 25 | 26 | aliases: ["file "], 27 | paramsDesc: ["specification file to build"], 28 | desc: "Given the specification file, build the respective code", 29 | 30 | setup: (cmd) => { 31 | // No additional argument required 32 | }, 33 | 34 | // Execute the run command 35 | run: async (argv, context) => { 36 | try { 37 | const specFile = argv.specfile; 38 | const fullpath = await getSpecFile(specFile); 39 | 40 | if( fullpath == null ) { 41 | OutputHandler.fatalError(`Missing valid spec file path : ${specFile}`, 44); 42 | return; 43 | } 44 | 45 | const relativePath = path.relative( path.resolve( config.spec_dir ), fullpath ); 46 | 47 | // Build the Subpath, relative to all varients, without file type 48 | const relativePathParse = path.parse(relativePath); 49 | // We remove the last trailing .spec from the filename, as that is not part of the "filetype" 50 | const subPath = relativePathParse.dir + "/" +relativePathParse.name.split(".").slice(0,-1).join("."); 51 | 52 | OutputHandler.standardGreen(`[System] Generating code from spec file: ${fullpath}`) 53 | if( config.personality ) { 54 | const remark = await personalityRemark(fullpath, config.personality, "Building a single code file"); 55 | OutputHandler.standardGreen(`[AI Remark] ${remark}`) 56 | } 57 | 58 | // Generate the code 59 | const generatedCode = await markdownSpecFileToCode(fullpath); 60 | 61 | // The path for the code, and test file output 62 | let codeFilePath = null; 63 | let testFilePath = null; 64 | 65 | // Handle according to language 66 | if( generatedCode.lang == "javascript" ) { 67 | codeFilePath = path.join( path.resolve(config.code_dir), subPath+".js" ); 68 | testFilePath = path.join( path.resolve(config.test_dir), subPath+".test.js"); 69 | } else if( generatedCode.lang == "java" ) { 70 | codeFilePath = path.join( path.resolve(config.code_dir), subPath+".java" ); 71 | // testFilePath = path.join( path.resolve(config.test_dir), subPath+".test.java"); 72 | } else if( generatedCode.lang == "html" ) { 73 | codeFilePath = path.join( path.resolve(config.code_dir), subPath+".html" ); 74 | // testFilePath = path.join( path.resolve(config.test_dir), subPath+".test.java"); 75 | } 76 | 77 | // Lets write the file accordingly 78 | OutputHandler.standardGreen("[AI] Writing source file :"+codeFilePath); 79 | await fs.promises.mkdir( path.dirname(codeFilePath), { recursive: true } ); 80 | await fs.promises.writeFile(codeFilePath, generatedCode.code, "utf8"); 81 | 82 | if( generatedCode.test ) { 83 | OutputHandler.standardGreen("[AI] Writing test file :"+testFilePath); 84 | await fs.promises.mkdir( path.dirname(testFilePath), { recursive: true } ); 85 | await fs.promises.writeFile(testFilePath, generatedCode.test, "utf8"); 86 | } 87 | 88 | if( config.personality ) { 89 | const remark = await personalityRemark(fullpath, config.personality, "Complete building a single code file"); 90 | OutputHandler.standardGreen(`[AI Remark] ${remark}`) 91 | } 92 | } catch(err) { 93 | OutputHandler.fatalError(err, 51); 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /src/cli/spec/infer.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------- 2 | // Dependencies 3 | //--------------------------------------------------- 4 | 5 | const markdownSpecFileSuggestion = require("../../codegen/markdownSpecFileSuggestion"); 6 | const OutputHandler = require("../OutputHandler"); 7 | const getSpecFile = require("../util/getSpecFile"); 8 | const inquirer = require('inquirer'); 9 | const markdownSpecFileChangeWithFeedback = require("../../codegen/markdownSpecFileChangeWithFeedback"); 10 | const getMarkdownObj = require("../../util/getMarkdownObj"); 11 | const diff = require("diff") 12 | const fs = require("fs") 13 | 14 | //--------------------------------------------------- 15 | // Command Definition 16 | //--------------------------------------------------- 17 | 18 | module.exports = { 19 | 20 | aliases: ["infer "], 21 | paramsDesc: ["specification file to make changes"], 22 | desc: "Sometimes you want to start with code, its alright. Lets update the specs accordingly", 23 | 24 | setup: (cmd) => { 25 | // No additional argument required 26 | }, 27 | 28 | // Execute the run command 29 | run: async (argv, context) => { 30 | try { 31 | const specFile = argv.specfile; 32 | const fullpath = await getSpecFile(specFile); 33 | 34 | if( fullpath == null ) { 35 | OutputHandler.fatalError(`Missing valid spec file path : ${specFile}`, 44); 36 | return; 37 | } 38 | const originalMd = await getMarkdownObj( fullpath ); 39 | 40 | OutputHandler.standardGreen("### ---") 41 | OutputHandler.standardGreen("### Generating AI suggestions for spec file (using code file) ... ") 42 | OutputHandler.standardGreen("### "+fullpath); 43 | OutputHandler.standardGreen("### ---") 44 | 45 | // @TODO 46 | 47 | } catch(err) { 48 | OutputHandler.fatalError(err, 51); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/cli/spec/suggest.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------- 2 | // 3 | // Dependencies 4 | // 5 | //--------------------------------------------------- 6 | 7 | const markdownSpecFileSuggestion = require("../../codegen/markdownSpecFileSuggestion"); 8 | const OutputHandler = require("../OutputHandler"); 9 | const getSpecFile = require("../util/getSpecFile"); 10 | const inquirer = require('inquirer'); 11 | const markdownSpecFileChangeWithFeedback = require("../../codegen/markdownSpecFileChangeWithFeedback"); 12 | const getMarkdownObj = require("../../util/getMarkdownObj"); 13 | const diff = require("diff") 14 | const fs = require("fs"); 15 | const config = require("../../core/config"); 16 | const personalityRemark = require("../../codegen/personalityRemark"); 17 | 18 | //--------------------------------------------------- 19 | // 20 | // Command Definition 21 | // 22 | //--------------------------------------------------- 23 | 24 | module.exports = { 25 | 26 | aliases: ["suggest "], 27 | paramsDesc: ["specification file to suggest changes"], 28 | desc: "Given the specification file path, suggest changes to be made", 29 | 30 | setup: (cmd) => { 31 | // No additional argument required 32 | }, 33 | 34 | // Execute the run command 35 | run: async (argv, context) => { 36 | try { 37 | const specFile = argv.specfile; 38 | const fullpath = await getSpecFile(specFile); 39 | 40 | if( fullpath == null ) { 41 | OutputHandler.fatalError(`Missing valid spec file path : ${specFile}`, 44); 42 | return; 43 | } 44 | 45 | OutputHandler.standardGreen("[System] Generating AI suggestions for spec file : "+fullpath) 46 | 47 | const suggestion = await markdownSpecFileSuggestion( fullpath ); 48 | OutputHandler.standard(`[AI Suggestions]\n${suggestion}`); 49 | 50 | if( config.personality ) { 51 | const remark = await personalityRemark(fullpath, config.personality, "Awaiting for instructions, on how to improve the spec file"); 52 | OutputHandler.standardGreen(`[AI Remark] ${remark}`) 53 | } 54 | OutputHandler.standardGreen("[System] Please provide instructions, on what changes would you like ... ") 55 | 56 | const promptRes = await inquirer.prompt([ 57 | { name:"instruction", default: "Apply all the suggested changes" } 58 | ]); 59 | 60 | const instruction = promptRes.instruction; 61 | 62 | OutputHandler.standardGreen("[System] Computing changes ... ") 63 | if( config.personality ) { 64 | const remark = await personalityRemark(fullpath, config.personality, `Comment on the following instructions provided '${instruction}'`); 65 | OutputHandler.standardGreen(`[AI Remark] ${remark}`) 66 | } 67 | 68 | const originalMd = await getMarkdownObj( fullpath ); 69 | const updatedSpec = await markdownSpecFileChangeWithFeedback( fullpath, suggestion, instruction ); 70 | 71 | const changeDelta = diff.diffLines(originalMd.content, updatedSpec); 72 | for(const segment of changeDelta) { 73 | if( segment.removed ) { 74 | OutputHandler.standardRed(segment.value.trim()); 75 | } else if( segment.added ) { 76 | OutputHandler.standardGreen(segment.value.trim()); 77 | } else { 78 | OutputHandler.standard(segment.value.trim()); 79 | } 80 | } 81 | 82 | OutputHandler.standardGreen("[System] Would you like to apply the above changes? ") 83 | 84 | const confirmRes = (await inquirer.prompt([ 85 | { name:"apply", default:false, type:"confirm" } 86 | ])).apply; 87 | 88 | if( config.personality ) { 89 | const remark = await personalityRemark(fullpath, config.personality, `Provide commentry, about completing '${instruction}'`); 90 | OutputHandler.standardGreen(`[AI Remark] ${remark}`) 91 | } 92 | 93 | if( confirmRes ) { 94 | await fs.promises.writeFile( fullpath, originalMd.frontmatter+"\n"+updatedSpec ); 95 | } 96 | } catch(err) { 97 | OutputHandler.fatalError(err, 51); 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /src/cli/util/getSpecFile.js: -------------------------------------------------------------------------------- 1 | const fileExist = require("../../util/fileExist") 2 | const config = require("../../core/config") 3 | const path = require("path") 4 | /** 5 | * Check and get the relative path (to the spec dir) of the spec file 6 | * and handle 2 major permutation of the paths 7 | * 8 | * - with or without .spec.md suffix 9 | * - with or without spec dir path 10 | */ 11 | async function getSpecFile(filepath) { 12 | let fullpath = path.resolve(filepath); 13 | if( await fileExist( fullpath ) ) { 14 | return fullpath; 15 | } 16 | 17 | fullpath = path.resolve(config.spec_dir, filepath); 18 | if( await fileExist( fullpath ) ) { 19 | return fullpath; 20 | } 21 | 22 | fullpath = path.resolve(filepath+".spec.md"); 23 | if( await fileExist( fullpath ) ) { 24 | return fullpath; 25 | } 26 | 27 | fullpath = path.resolve(config.spec_dir, filepath+".spec.md"); 28 | if( await fileExist( fullpath ) ) { 29 | return fullpath; 30 | } 31 | 32 | // No valid path, return null 33 | return null; 34 | } 35 | 36 | module.exports = getSpecFile -------------------------------------------------------------------------------- /src/codegen/html/specToCodeFile.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../../openai/cachedCompletion"); 6 | const cachePath = require("../promptParts/cachePath"); 7 | const fullSpecPrefix = require("../promptParts/fullSpecPrefix"); 8 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj"); 9 | const removeSuffixIfPresent = require("../promptParts/removeSuffixIfPresent"); 10 | 11 | //-------------------------------------------------- 12 | // Implementation 13 | //-------------------------------------------------- 14 | 15 | /** 16 | * Given the JS spec obj, generate the full JS module 17 | * 18 | * @param {Object} specObj object representing the JS module 19 | * @param {Function} prgCallbck function, used to log the various incremental status 20 | * 21 | * @return Fully generated code 22 | */ 23 | async function specToCodeFile( specObj, prgCallbck = function() {} ) { 24 | // Get the various config values, with defaults 25 | const { _pLang, _humanLabel, _assistantLabel } = normalizeSpecObj( specObj ); 26 | 27 | // Full spec prefix 28 | const specPrefix = fullSpecPrefix( specObj ); 29 | 30 | // Perform the Gen1 codegen 31 | prgCallbck(`FunctionModuleCodegen for '${specObj.name}' (Gen1)`); 32 | const g1_prompt = [ 33 | specPrefix, 34 | "", 35 | `${_humanLabel}:`, 36 | `Please generate the ${_pLang} code, with the complete implementation according the spec. Without comments.`, 37 | "", 38 | `${_assistantLabel}:`, 39 | "```", 40 | "" 41 | ].join("\n") 42 | const g1_codegen = removeSuffixIfPresent( await cachedCompletion(g1_prompt, cachePath(specObj, "FunctionModuleCodegen")), "```"); 43 | 44 | // Perform the Gen2 codegen 45 | prgCallbck(`FunctionModuleCodegen for '${specObj.name}' (Gen2)`); 46 | const g2_prompt = [ 47 | // Since g1 prompt already ends with \n 48 | g1_prompt + g1_codegen, 49 | "```", 50 | "", 51 | `${_humanLabel}:`, 52 | `Please improve the code. Improve readability.`, 53 | `Fix any potential bugs, and handle edge cases.`, 54 | `While simplifying and reducing the number of code steps and lines involved if possible.`, 55 | "", 56 | `${_assistantLabel}:`, 57 | "```", 58 | "" 59 | ].join("\n") 60 | const g2_codegen = removeSuffixIfPresent( await cachedCompletion(g2_prompt, cachePath(specObj, "FunctionModuleCodegen")), "```"); 61 | 62 | // Return gen 2 result 63 | return g2_codegen; 64 | 65 | // // Perform Gen3 codegen 66 | // prgCallbck(`FunctionModuleCodegen for '${specObj.name}' (Gen3)`); 67 | // const g3_prompt = [ 68 | // specPrefix, 69 | // "", 70 | // `${_humanLabel}:`, 71 | // `Please generate the ${_pLang} code`, 72 | // "", 73 | // `${_assistantLabel}:`, 74 | // "```", 75 | // g2_codegen, 76 | // "```", 77 | // "", 78 | // `${_humanLabel}:`, 79 | // `Please improve the code, and add code comments, including detailed documentation for all functions.`, 80 | // "Simplifying and reducing the number of code steps and lines involved if possible.", 81 | // "Add code comment inside the function, to describe the process.", 82 | // "", 83 | // `${_assistantLabel}:`, 84 | // "```", 85 | // "" 86 | // ].join("\n"); 87 | // const g3_codegen = removeSuffixIfPresent( await cachedCompletion(g3_prompt, cachePath(specObj, "FunctionModuleCodegen")), "```"); 88 | 89 | // // Perform Gen4 codegen 90 | // prgCallbck(`FunctionModuleCodegen for '${specObj.name}' (Gen4)`); 91 | // const g4_prompt = [ 92 | // specPrefix, 93 | // "", 94 | // `${_humanLabel}:`, 95 | // `Please generate the ${_pLang} code`, 96 | // "", 97 | // `${_assistantLabel}:`, 98 | // "```", 99 | // g2_codegen, 100 | // "```", 101 | // "", 102 | // `${_humanLabel}:`, 103 | // `Please improve overall readability of the code, and add code comments.`, 104 | // "", 105 | // `${_assistantLabel}:`, 106 | // "```", 107 | // "" 108 | // ].join("\n"); 109 | // const g4_codegen = removeSuffixIfPresent( await cachedCompletion(g3_prompt, cachePath(specObj, "FunctionModuleCodegen")), "```"); 110 | 111 | // // Return gen 4 result 112 | // return g4_codegen; 113 | } 114 | module.exports = specToCodeFile; -------------------------------------------------------------------------------- /src/codegen/javascript/specToFunctionModule.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../../openai/cachedCompletion"); 6 | const cachePath = require("../promptParts/cachePath"); 7 | const fullSpecPrefix = require("../promptParts/fullSpecPrefix"); 8 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj"); 9 | const removeSuffixIfPresent = require("../promptParts/removeSuffixIfPresent"); 10 | 11 | //-------------------------------------------------- 12 | // Implementation 13 | //-------------------------------------------------- 14 | 15 | /** 16 | * Given the JS spec obj, generate the full JS module 17 | * 18 | * @param {Object} specObj object representing the JS module 19 | * @param {Function} prgCallbck function, used to log the various incremental status 20 | * 21 | * @return Fully generated code 22 | */ 23 | async function specToFunctionModule( specObj, prgCallbck = function() {} ) { 24 | // Get the various config values, with defaults 25 | const { _pLang, _humanLabel, _assistantLabel } = normalizeSpecObj( specObj ); 26 | 27 | // Full spec prefix 28 | const specPrefix = fullSpecPrefix( specObj ); 29 | 30 | // Perform the Gen1 codegen 31 | prgCallbck(`FunctionModuleCodegen for '${specObj.name}' (Gen1)`); 32 | const g1_prompt = [ 33 | specPrefix, 34 | "", 35 | `${_humanLabel}:`, 36 | `Please generate the ${_pLang} module code, with the complete implementation according the spec. Without comments.`, 37 | "", 38 | `${_assistantLabel}:`, 39 | "```", 40 | "" 41 | ].join("\n") 42 | const g1_codegen = removeSuffixIfPresent( await cachedCompletion(g1_prompt, cachePath(specObj, "FunctionModuleCodegen")), "```"); 43 | 44 | // Perform the Gen2 codegen 45 | prgCallbck(`FunctionModuleCodegen for '${specObj.name}' (Gen2)`); 46 | const g2_prompt = [ 47 | // Since g1 prompt already ends with \n 48 | g1_prompt + g1_codegen, 49 | "```", 50 | "", 51 | `${_humanLabel}:`, 52 | `Please improve the code. Improve readability.`, 53 | `Fix any potential bugs, and handle edge cases.`, 54 | `While simplifying and reducing the number of code steps and lines involved if possible.`, 55 | "", 56 | `${_assistantLabel}:`, 57 | "```", 58 | "" 59 | ].join("\n") 60 | const g2_codegen = removeSuffixIfPresent( await cachedCompletion(g2_prompt, cachePath(specObj, "FunctionModuleCodegen")), "```"); 61 | 62 | // Perform Gen3 codegen 63 | prgCallbck(`FunctionModuleCodegen for '${specObj.name}' (Gen3)`); 64 | const g3_prompt = [ 65 | specPrefix, 66 | "", 67 | `${_humanLabel}:`, 68 | `Please generate the ${_pLang} module code`, 69 | "", 70 | `${_assistantLabel}:`, 71 | "```", 72 | g2_codegen, 73 | "```", 74 | "", 75 | `${_humanLabel}:`, 76 | `Please improve the code, and add code comments, including detailed documentation for all functions.`, 77 | "Simplifying and reducing the number of code steps and lines involved if possible.", 78 | "Add code comment inside the function, to describe the process.", 79 | "Fully explain and describe in detail any regex that is used.", 80 | "", 81 | `${_assistantLabel}:`, 82 | "```", 83 | "" 84 | ].join("\n"); 85 | const g3_codegen = removeSuffixIfPresent( await cachedCompletion(g3_prompt, cachePath(specObj, "FunctionModuleCodegen")), "```"); 86 | 87 | // Perform Gen4 codegen 88 | prgCallbck(`FunctionModuleCodegen for '${specObj.name}' (Gen4)`); 89 | const g4_prompt = [ 90 | specPrefix, 91 | "", 92 | `${_humanLabel}:`, 93 | `Please generate the ${_pLang} module code`, 94 | "", 95 | `${_assistantLabel}:`, 96 | "```", 97 | g2_codegen, 98 | "```", 99 | "", 100 | `${_humanLabel}:`, 101 | `Please improve overall readability of the code, and the code comments.`, 102 | "", 103 | `${_assistantLabel}:`, 104 | "```", 105 | "" 106 | ].join("\n"); 107 | const g4_codegen = removeSuffixIfPresent( await cachedCompletion(g3_prompt, cachePath(specObj, "FunctionModuleCodegen")), "```"); 108 | 109 | // Return gen 4 result 110 | return g4_codegen; 111 | } 112 | module.exports = specToFunctionModule; -------------------------------------------------------------------------------- /src/codegen/javascript/specToFunctionModuleUnitTest.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const config = require("../../core/config"); 6 | const path = require("path"); 7 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj"); 8 | const specExtraction = require("../segment/specExtraction"); 9 | 10 | //-------------------------------------------------- 11 | // Implementation 12 | //-------------------------------------------------- 13 | 14 | /** 15 | * Given the JS spec obj, generate the full JS module 16 | * 17 | * @param {Object} specObj object representing the JS module 18 | * 19 | * @return Fully generated code 20 | */ 21 | async function specToFunctionModuleUnitTest( specObj ) { 22 | // Get the various config values, with defaults 23 | const { _pLang } = normalizeSpecObj( specObj ); 24 | 25 | // Get the code file, and test file path, currently only JS should be supported 26 | let subPath = specObj.namespace.split(".").join("/")+"/"+specObj.name; 27 | let codeFilePath = path.join(config.code_dir, subPath+".js"); 28 | let testFilePath = path.join(config.test_dir, subPath+".test.js"); 29 | 30 | // Get the g1 minified code 31 | let g1_codegen = await specExtraction( 32 | specObj, 33 | [ 34 | `The code relative to the test file, will be at: `+path.relative(testFilePath, codeFilePath), 35 | `Please generate the ${_pLang} module unit test file, with multiple scenerios, according to the spec`, 36 | `Use the assert library for the test` 37 | ].join("\n"), 38 | "FunctionModuleCodegen" 39 | ); 40 | 41 | return g1_codegen; 42 | } 43 | module.exports = specToFunctionModuleUnitTest; -------------------------------------------------------------------------------- /src/codegen/markdownSpecFileChangeWithFeedback.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const parseMD = require('parse-md').default 6 | const fs = require("fs"); 7 | const path = require('path'); 8 | const specChangeUsingFeedback = require('./segment/specChangeUsingFeedback'); 9 | 10 | //-------------------------------------------------- 11 | // Implementation 12 | //-------------------------------------------------- 13 | 14 | /** 15 | * Given the markdown details, generate the full code file 16 | * 17 | * @param {String} filePath for the full markdown file 18 | * 19 | * @return Generated code string, wrapped in a promise 20 | */ 21 | async function markdownSpecFileChangeWithFeedback( filePath, aiSuggestion, userFeedback, prgCallbck = function() {} ) { 22 | // Read the file, and split out the metadata + content 23 | let fullFileData = await fs.promises.readFile( filePath, "utf8" ); 24 | let { metadata, content } = parseMD(fullFileData); 25 | 26 | // Guess the name if not set, from the first segment of the filename 27 | metadata.name = metadata.name || path.basename( filePath ).split(".")[0]; 28 | metadata.spec = content; 29 | 30 | // Forward it to the actual function that processes it 31 | return await specChangeUsingFeedback( metadata, aiSuggestion, userFeedback ); 32 | } 33 | module.exports = markdownSpecFileChangeWithFeedback; 34 | -------------------------------------------------------------------------------- /src/codegen/markdownSpecFileSuggestion.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const parseMD = require('parse-md').default 6 | const fs = require("fs"); 7 | const path = require('path'); 8 | const specChangeSuggestion = require('./segment/specChangeSuggestion'); 9 | 10 | //-------------------------------------------------- 11 | // Implementation 12 | //-------------------------------------------------- 13 | 14 | /** 15 | * Given the markdown details, generate the full code file 16 | * 17 | * @param {String} filePath for the full markdown file 18 | * 19 | * @return Generated code string, wrapped in a promise 20 | */ 21 | async function markdownSpecFileSuggestion( filePath, prgCallbck = function() {} ) { 22 | // Read the file, and split out the metadata + content 23 | let fullFileData = await fs.promises.readFile( filePath, "utf8" ); 24 | let { metadata, content } = parseMD(fullFileData); 25 | 26 | // Guess the name if not set, from the first segment of the filename 27 | metadata.name = metadata.name || path.basename( filePath ).split(".")[0]; 28 | metadata.spec = content; 29 | 30 | // Forward it to the actual function that processes it 31 | return await specChangeSuggestion( metadata ); 32 | } 33 | module.exports = markdownSpecFileSuggestion; 34 | 35 | //-------------------------------------------------- 36 | // Direct calling support 37 | //-------------------------------------------------- 38 | 39 | // Special direct execution handling 40 | // https://stackoverflow.com/questions/6398196/detect-if-called-through-require-or-directly-by-command-line 41 | if( require.main === module ) { 42 | (async function() { 43 | 44 | // Argument check 45 | const reqPrompt = process.argv[2]; 46 | if( reqPrompt == null ) { 47 | console.error( "[ERROR] Missing request prompt as first argument" ); 48 | console.error( `[ERROR] Example: node ${process.argv[1]} "./file/path/to/class.spec.md"` ); 49 | process.exit(1); 50 | } 51 | 52 | // Response 53 | const resp = await markdownSpecFileSuggestion(reqPrompt, (msg) => { console.log(`- ${msg}`)}); 54 | console.log(resp); 55 | })(); 56 | } -------------------------------------------------------------------------------- /src/codegen/markdownSpecFileToCode.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const parseMD = require('parse-md').default 6 | const fs = require("fs"); 7 | const path = require('path'); 8 | const markdownSpecToCode = require("./markdownSpecToCode"); 9 | 10 | //-------------------------------------------------- 11 | // Implementation 12 | //-------------------------------------------------- 13 | 14 | /** 15 | * Given the markdown details, generate the full code file 16 | * 17 | * @param {String} filePath for the full markdown file 18 | * 19 | * @return Generated code string, wrapped in a promise 20 | */ 21 | async function markdownSpecFileToCode( filePath, prgCallbck = function() {} ) { 22 | // Read the file, and split out the metadata + content 23 | let fullFileData = await fs.promises.readFile( filePath, "utf8" ); 24 | let { metadata, content } = parseMD(fullFileData); 25 | 26 | // Guess the name if not set, from the first segment of the filename 27 | metadata.name = metadata.name || path.basename( filePath ).split(".")[0]; 28 | 29 | // Forward it to the actual function that processes it 30 | return await markdownSpecToCode( metadata, content, prgCallbck ); 31 | } 32 | module.exports = markdownSpecFileToCode; 33 | 34 | //-------------------------------------------------- 35 | // Direct calling support 36 | //-------------------------------------------------- 37 | 38 | // Special direct execution handling 39 | // https://stackoverflow.com/questions/6398196/detect-if-called-through-require-or-directly-by-command-line 40 | if( require.main === module ) { 41 | (async function() { 42 | 43 | // Argument check 44 | const reqPrompt = process.argv[2]; 45 | if( reqPrompt == null ) { 46 | console.error( "[ERROR] Missing request prompt as first argument" ); 47 | console.error( `[ERROR] Example: node ${process.argv[1]} "./file/path/to/class.spec.md"` ); 48 | process.exit(1); 49 | } 50 | 51 | // Response 52 | const resp = await markdownSpecFileToCode(reqPrompt, (msg) => { console.log(`- ${msg}`)}); 53 | console.log(resp); 54 | })(); 55 | } -------------------------------------------------------------------------------- /src/codegen/markdownSpecToCode.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const normalizeSpecObj = require("./promptParts/normalizeSpecObj") 6 | const specToFullClassCode = require("./prototype/specToFullClassCode"); 7 | const specToFunctionModule = require("./javascript/specToFunctionModule"); 8 | const specToFunctionModuleUnitTest = require("./javascript/specToFunctionModuleUnitTest"); 9 | const specToCodeFile = require("./html/specToCodeFile"); 10 | 11 | //-------------------------------------------------- 12 | // Implementation 13 | //-------------------------------------------------- 14 | 15 | /** 16 | * Given the markdown details, generate the full code file 17 | * 18 | * @param {Object} specObj, this is typyically the metadata involved 19 | * @param {String} mdStr, full markdown file containing the given class spec 20 | * 21 | * @return Generated code string, wrapped in a promise 22 | */ 23 | async function markdownSpecToCode( specObj, mdStr, prgCallbck = function() {} ) { 24 | // Safety check on specObj, we require at minimum type, and name 25 | if( specObj == null || specObj.name == null || specObj.type == null ) { 26 | console.warn([ 27 | "Missing valid markdown specification metadata, at minimum it should include name, and type for example : ", 28 | "---", 29 | "type: java", 30 | "name: ClassName", 31 | "namespace: package.namespace", 32 | "---" 33 | ].join("\n")) 34 | throw "Missing valid specification metadata, see console logs for example format"; 35 | } 36 | 37 | // Get the various config values, with defaults 38 | const { type, _pLang, _subType } = normalizeSpecObj(specObj); 39 | 40 | // Lets first embed the mdStr, as a spec, field 41 | specObj.spec = mdStr; 42 | 43 | // Perform conversion according to language type 44 | if( _pLang == "java" ) { 45 | // Lets generate the full class code 46 | return { 47 | code: await specToFullClassCode( specObj, {} ), 48 | test: null, //@TODO (not supported now), 49 | lang: _pLang, 50 | type: _subType 51 | } 52 | } else if( _pLang == "javascript" ) { 53 | let ret = { 54 | code: await specToFunctionModule( specObj, prgCallbck ), 55 | test: null, // , 56 | lang: _pLang, 57 | type: _subType 58 | } 59 | 60 | if( specObj.test ) { 61 | ret.test = await specToFunctionModuleUnitTest( specObj, prgCallbck ) 62 | } 63 | return ret; 64 | } else if ( _pLang == "html" ) { 65 | let ret = { 66 | code: await specToCodeFile( specObj, prgCallbck ), 67 | test: null, // , 68 | lang: _pLang, 69 | type: _subType 70 | } 71 | return ret; 72 | } 73 | 74 | // Throw for unsupported language 75 | throw "Unsupported language type / file type : "+_pLang+" / "+_subType; 76 | } 77 | module.exports = markdownSpecToCode; 78 | -------------------------------------------------------------------------------- /src/codegen/personalityRemark.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const parseMD = require('parse-md').default 6 | const fs = require("fs"); 7 | const path = require('path'); 8 | const specExtraction = require('./segment/specExtraction'); 9 | 10 | //-------------------------------------------------- 11 | // Implementation 12 | //-------------------------------------------------- 13 | 14 | /** 15 | * Given the markdown details, generate the full code file 16 | * 17 | * @param {String} filePath for the full markdown file 18 | * 19 | * @return Generated code string, wrapped in a promise 20 | */ 21 | async function personalityRemark( filePath, personality = "Sarcastic & Sassy", remarkType = "Building the code according to the spec" ) { 22 | // Read the file, and split out the metadata + content 23 | let fullFileData = await fs.promises.readFile( filePath, "utf8" ); 24 | let { metadata, content } = parseMD(fullFileData); 25 | 26 | // Guess the name if not set, from the first segment of the filename 27 | metadata.name = metadata.name || path.basename( filePath ).split(".")[0]; 28 | metadata.spec = content; 29 | 30 | // Forward it to the actual function that processes it 31 | return await specExtraction( metadata, `Please provide a ${personality} remark or commentry for ${remarkType}`, "personality-remark" ) 32 | } 33 | module.exports = personalityRemark; 34 | 35 | //-------------------------------------------------- 36 | // Direct calling support 37 | //-------------------------------------------------- 38 | 39 | // Special direct execution handling 40 | // https://stackoverflow.com/questions/6398196/detect-if-called-through-require-or-directly-by-command-line 41 | if( require.main === module ) { 42 | (async function() { 43 | 44 | // Argument check 45 | const reqPrompt = process.argv[2]; 46 | if( reqPrompt == null ) { 47 | console.error( "[ERROR] Missing request prompt as first argument" ); 48 | console.error( `[ERROR] Example: node ${process.argv[1]} "./file/path/to/class.spec.md"` ); 49 | process.exit(1); 50 | } 51 | 52 | // Response 53 | const resp = await personalityRemark(reqPrompt, (msg) => { console.log(`- ${msg}`)}); 54 | console.log(resp); 55 | })(); 56 | } -------------------------------------------------------------------------------- /src/codegen/promptParts/blockWrapper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wrap the data within a block delimiter, 3 | * adapted according to the input data, to avoid accidental "block escape" 4 | * 5 | * @param {String} type 6 | * @param {String} data 7 | */ 8 | function blockWrapper(type, data) { 9 | // Trim the data provided 10 | data = data.trim(); 11 | 12 | // Handle markdown escape 13 | if( data.indexOf("```") >= 0 ) { 14 | // Block wrapper line, intentionally split apart, so that this code can eventually be.... 15 | // parsed by the code-ai-project 16 | let codeLineBreak = "<<" + "{{" + "~~" + "}}" + ">>"; 17 | 18 | // Wrap it using special character code lines 19 | return [ 20 | `${type} (between the \`${codeLineBreak}\` lines) :`, 21 | codeLineBreak, 22 | data.replace(/\<\<\{\{/g, "\\<\\<\\{\\{").replace(/\>\>\}\}/g, "\\>\\>\\}\\}"), 23 | codeLineBreak 24 | ].join("\n") 25 | } 26 | 27 | // Wrap it using markdown code block 28 | return [ 29 | `${type} :`, 30 | "```", 31 | data, 32 | "```" 33 | ].join("\n") 34 | } 35 | module.exports = blockWrapper; -------------------------------------------------------------------------------- /src/codegen/promptParts/cachePath.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const normalizeSpecObj = require("./normalizeSpecObj") 6 | 7 | //-------------------------------------------------- 8 | // Implementation 9 | //-------------------------------------------------- 10 | 11 | /** 12 | * Compute the jsonl file path to use, given the specObj, and operation type 13 | * 14 | * While taking into account the cacheMode. 15 | * Returns null, if not public / private 16 | * 17 | * @param {*} specObj 18 | * @param {*} opType 19 | * 20 | * @return File path as a string 21 | */ 22 | module.exports = function cachePath( specObj, opType ) { 23 | // Get the various config values, with defaults 24 | // used to build the cacheKey 25 | const { type, name, cacheMode, _pLang } = normalizeSpecObj(specObj); 26 | 27 | // Remove whitespaces, using dash 28 | // const typeWithDash = type.replace(/ /g, "-"); 29 | const fPath = `codegen/${_pLang}/${opType}.${name}.jsonl`; 30 | 31 | if( cacheMode == "public" ) { 32 | return fPath 33 | } else if( cacheMode == "private" ) { 34 | return ".private/"+fPath; 35 | } 36 | 37 | return null; 38 | } -------------------------------------------------------------------------------- /src/codegen/promptParts/fullSpecPrefix.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const getTokenCount = require("../../openai/getTokenCount"); 6 | const blockWrapper = require("./blockWrapper") 7 | const normalizeSpecObj = require("./normalizeSpecObj") 8 | 9 | //-------------------------------------------------- 10 | // Implementation 11 | //-------------------------------------------------- 12 | 13 | /** 14 | * Generate the full opening prompt, which describes the current code specification. 15 | * Excluding the opening "human:" prompt. 16 | * 17 | * Given the following parameter in the class specObj 18 | * 19 | * - type : Programming language (eg. java, javascript) 20 | * - name : Name of the class to generate 21 | * - namespace : package namespace of the class 22 | * - spec : Detailed textual description describing the class 23 | * 24 | * @param {Object} specObj 25 | * 26 | * @return Generate the suggested changes to be done by the assistant 27 | */ 28 | function fullSpecPrefix( specObj ) { 29 | 30 | // Get the various specObj values, with defaults 31 | const { _pLang, _subType, name, spec, namespace } = normalizeSpecObj(specObj); 32 | 33 | // The prompt to use 34 | // PS: We use tabs > spaces to reduce token count !!! 35 | let promptPrefixArr = [ 36 | `Code indentation should be done with tabs (unless minified).`, 37 | `The ${_pLang} ${_subType} can be described with the following details`, 38 | "", 39 | `${_subType} name: ${name}`, 40 | ]; 41 | 42 | // Add the language specific stuff 43 | if( _pLang == "java" ) { 44 | if (namespace.length > 0) { 45 | promptPrefixArr.push(`Package Namespace: ${specObj.namespace}`); 46 | } 47 | } else if( _pLang == "javascript" ) { 48 | promptPrefixArr.push(`Module Type: ${specObj.moduleType}`); 49 | } 50 | 51 | // Add doctype (if set) 52 | if( specObj.docType ) { 53 | promptPrefixArr.push(`Documentation Style: ${specObj.docType}`); 54 | } 55 | 56 | // Textual description in block wrapper 57 | const desc = blockWrapper(`${_subType} textual description`, spec); 58 | promptPrefixArr.push(""); 59 | promptPrefixArr.push(desc); 60 | 61 | // Build the full prefix 62 | let promptPrefix = promptPrefixArr.join('\n'); 63 | 64 | // Token length safety 65 | if( getTokenCount(promptPrefix) > 1500 ) { 66 | throw "[[Token Limit Error]] Class specification is too large, reduce the main class specification description for : "+name; 67 | } 68 | 69 | // Return the full prompt prefix 70 | return promptPrefix; 71 | } 72 | 73 | module.exports = fullSpecPrefix; -------------------------------------------------------------------------------- /src/codegen/promptParts/getAgentLabels.js: -------------------------------------------------------------------------------- 1 | // Labels to use 2 | const human = [ 3 | "human", 4 | "instructor", 5 | "developer", 6 | 7 | "Human", 8 | "Instructor", 9 | "Developer" 10 | ]; 11 | const assistant = [ 12 | "assitant", 13 | "Assitant", 14 | "AI assistant", 15 | "AI Assistant", 16 | "AI Agent", 17 | "AI agent", 18 | "AI" 19 | ] 20 | 21 | /** 22 | * Given the spec object, get the respective human and assitant labels, 23 | * We intentionally rotate among a few labels to avoid overtraining a keyword. 24 | * 25 | * @param {Object} specObj 26 | */ 27 | module.exports = function getAgentLabels( specObj ) { 28 | let keyId = ((specObj && specObj.name) || "").length; 29 | return { 30 | human: human[ keyId % human.length ] || human[0], 31 | assistant: assistant[ keyId % assistant.length ] || assistant[0] 32 | } 33 | } -------------------------------------------------------------------------------- /src/codegen/promptParts/normalizeSpecObj.js: -------------------------------------------------------------------------------- 1 | const getAgentLabels = require("./getAgentLabels") 2 | 3 | /** 4 | * Normalize a specification object, type, name, namespace, and spec values to sane defaults. 5 | * 6 | * @param {*} config 7 | * @returns 8 | */ 9 | module.exports = function normalizeSpecObj( config ) { 10 | // Check self reference cache 11 | if( config._cacheObj && config._cacheObj.self == config) { 12 | return config; 13 | } 14 | 15 | // Get the various config values, with defaults 16 | config.name = (config.name || "HelloWorld").trim(); 17 | config.namespace = (config.namespace || "").trim(); 18 | config.spec = (config.spec || "Example hello world class, which echos 'hello world' when initialized.").trim(); 19 | 20 | // Cache flag used to indicate its a private codegen, and should not be saved within the prompt history cache 21 | config.cacheMode = (config.cacheMode || "public").trim(); 22 | 23 | // Get the type 24 | let type = (config.type || "java").trim().toLowerCase(); 25 | config.type = type; 26 | 27 | // Get the programming language 28 | let _pLang = type.split(" ")[0]; 29 | config._pLang = _pLang; 30 | 31 | // And the module type 32 | let _subType = type.slice(_pLang.length).trim(); 33 | if( _subType == null || _subType == "") { 34 | _subType = "class"; 35 | } 36 | config._subType = _subType; 37 | 38 | // Add useful labels for internal use 39 | const { human, assistant } = getAgentLabels(config); 40 | config._humanLabel = human; 41 | config._assistantLabel = assistant; 42 | 43 | // Handle language specific default 44 | if( _pLang == "javascript" ) { 45 | config.moduleType = (config.moduleType || "CJS").toUpperCase() 46 | config.docType = (config.docType || "JSDoc") 47 | } else if( _pLang == "java" ) { 48 | config.docType = (config.docType || "JavaDoc") 49 | } 50 | 51 | // Caching object, used to cache multiple sub operaitons 52 | config._cacheObj = config._cacheObj || {}; 53 | 54 | // Cache ownself 55 | config._cacheObj.self = config; 56 | 57 | // And return it 58 | return config; 59 | } -------------------------------------------------------------------------------- /src/codegen/promptParts/removeSuffixIfPresent.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given the input text, trim the suffix if its present 3 | * @param {String} text 4 | * @param {String} suffix 5 | * @returns 6 | */ 7 | module.exports = function removeSuffixIfPresent(text, suffix = "```") { 8 | text = text.trim(); 9 | 10 | // Sometimes it has the suffix 11 | if( text.endsWith(suffix) ) { 12 | return text.slice(0, -suffix.length).trim() 13 | } 14 | // Sometimes it reduces the suffix by 1 character 15 | if( text.endsWith(suffix.slice(-1)) ) { 16 | return text.slice(0, -(suffix.length-1)).trim() 17 | } 18 | 19 | // Sometimes it dun 20 | return text.trim(); 21 | } -------------------------------------------------------------------------------- /src/codegen/promptParts/simpleAndSafeMinify.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given two string parameters 3 | * - inData : containing the code, which needs to be minified 4 | * - inType : the programming language category either "cstyle" or "xml" 5 | * 6 | * Attempt to safely minify the given data code, while stripping comments in a safe way. 7 | * The goal isn't to get the perfect minified code (which we will use another tool), but to safely remove whitespace and comments when possible. And reduce the overall character count. 8 | * 9 | * @param {string} inData - The code to minify 10 | * @param {string} inType - The programming language category either "cstyle" or "xml" 11 | * 12 | * @returns {string} The minified code 13 | */ 14 | const simpleAndSafeMinify = (inData, inType) => { 15 | let outData = ''; 16 | if (inType === 'xml') { 17 | outData = simpleAndSafeMinifyXML(inData); 18 | } else if (inType === 'cstyle') { 19 | outData = simpleAndSafeMinifyCStyle(inData); 20 | } 21 | return outData.trim(); 22 | }; 23 | 24 | /** 25 | * Handle minification of XML code 26 | * 27 | * @param {string} inData - The code to minify 28 | * 29 | * @returns {string} The minified code 30 | */ 31 | const simpleAndSafeMinifyXML = (inData) => { 32 | // Find and remove blocks 33 | const commentRegex = //g; 34 | let outData = inData.replace(commentRegex, ''); 35 | 36 | // Find and remove whitespaces between blocks (check for ">" and "<") 37 | const whitespaceRegex = />\s+<'); 39 | 40 | // Trim the final string 41 | return outData.split("\n").map((val)=>{return val.trim()}).join("\n").trim(); 42 | }; 43 | 44 | /** 45 | * Handle minification of cstyle code 46 | * 47 | * @param {string} inData - The code to minify 48 | * 49 | * @returns {string} The minified code 50 | */ 51 | const simpleAndSafeMinifyCStyle = (inData) => { 52 | // Find 1 string block which can be wrapped with either ` or ' or " quotes. 53 | const strRegex = /('.*'|".*"|`.*`)/; 54 | const strMatch = inData.match(strRegex); 55 | 56 | // If string block is found, avoi modifying the string block 57 | if (strMatch) { 58 | // Leave the string block untouched 59 | const strStart = strMatch.index; 60 | const strEnd = strStart + strMatch[0].length; 61 | const beforeStr = inData.slice(0, strStart); 62 | const afterStr = inData.slice(strEnd); 63 | 64 | // Recursively minify the code before and after the string block 65 | return simpleAndSafeMinifyCStyle(beforeStr) + strMatch[0] + simpleAndSafeMinifyCStyle(afterStr); 66 | } 67 | 68 | // using regex, remove any trailing // comments for each line 69 | let outData = inData.replace( /\/\/.*\n/g, ''); 70 | 71 | // Remove / * comment blocks * / 72 | outData = outData.replace( /\/\*.*?\*\//g , ''); 73 | 74 | // Replace multiple whitespace (excluding new lines) with a single space 75 | outData = outData.replace( /[ \t]+/g , ' '); 76 | 77 | // Safely handle end of statements 78 | outData = outData.replace( /\;\s*/g , ';'); 79 | 80 | // Safely handle assignments 81 | outData = outData.replace( / =/g , '='); 82 | outData = outData.replace( /= /g , '='); 83 | 84 | // Trim the final string 85 | return outData.split("\n").map((val)=>{return val.trim()}).join("\n").trim(); 86 | }; 87 | 88 | module.exports = simpleAndSafeMinify; -------------------------------------------------------------------------------- /src/codegen/prototype/codeConversion.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../../openai/cachedCompletion") 6 | const blockWrapper = require("../promptParts/blockWrapper") 7 | const removeSuffixIfPresent = require("../promptParts/removeSuffixIfPresent") 8 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj") 9 | const cachePath = require("../promptParts/cachePath") 10 | 11 | //-------------------------------------------------- 12 | // Implementation 13 | //-------------------------------------------------- 14 | 15 | /** 16 | * Given the following code, convert from one format to another 17 | * @param {String} code to convert 18 | * @param {String} from stated type 19 | * @param {String} to target type 20 | * @param {String} specObj, if not set the code will be treated as an ambigious snippet 21 | * @returns 22 | */ 23 | async function codeConversion( code, from, to, specObj = { name: "VariousSnippet", type: "snippet" } ) { 24 | 25 | // Get the various config values, with defaults 26 | // used to build the cacheKey 27 | const { type, name, cacheMode } = normalizeSpecObj(specObj); 28 | 29 | // Textual description in block wrapper 30 | const codeBlock = blockWrapper(`${from}`, code); 31 | 32 | // The prompt to use 33 | let fullPrompt = [ 34 | `Convert the following`, 35 | "", 36 | codeBlock, 37 | "", 38 | `To ${to}`, 39 | "", 40 | "```", 41 | "" 42 | ].join("\n") 43 | 44 | // Generate it 45 | let resp = await cachedCompletion(fullPrompt, cachePath( specObj, "CodeConv" )); 46 | 47 | // Remove the ending "```" 48 | return removeSuffixIfPresent(resp, "```"); 49 | } 50 | 51 | module.exports = codeConversion; -------------------------------------------------------------------------------- /src/codegen/prototype/codeExtraction.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../../openai/cachedCompletion") 6 | const blockWrapper = require("../promptParts/blockWrapper") 7 | const removeSuffixIfPresent = require("../promptParts/removeSuffixIfPresent") 8 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj") 9 | const cachePath = require("../promptParts/cachePath") 10 | 11 | //-------------------------------------------------- 12 | // Implementation 13 | //-------------------------------------------------- 14 | 15 | /** 16 | * Given the following code, convert from one format to another 17 | * @param {String} code to convert 18 | * @param {String} from stated type 19 | * @param {String} to segment that needs to be extracted 20 | * @param {String} specObj, if not set the code will be treated as an ambigious snippet 21 | * @returns 22 | */ 23 | async function codeExtraction( code, from, to, specObj = { name: "VariousSnippet", type: "snippet" } ) { 24 | 25 | // Get the various config values, with defaults 26 | // used to build the cacheKey 27 | const { type, name, cacheMode } = normalizeSpecObj(specObj); 28 | 29 | // Textual description in block wrapper 30 | const codeBlock = blockWrapper(`${from}`, code); 31 | 32 | // The prompt to use 33 | let fullPrompt = [ 34 | `Extract from the following`, 35 | "", 36 | codeBlock, 37 | "", 38 | `The ${to}`, 39 | "", 40 | "```", 41 | "" 42 | ].join("\n") 43 | 44 | // Generate it 45 | let resp = await cachedCompletion(fullPrompt, cachePath( specObj, "CodeExtraction" )); 46 | 47 | // Remove the ending "```" 48 | return removeSuffixIfPresent(resp, "```"); 49 | } 50 | 51 | module.exports = codeExtraction; -------------------------------------------------------------------------------- /src/codegen/prototype/codeMinify.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../../openai/cachedCompletion") 6 | const blockWrapper = require("../promptParts/blockWrapper") 7 | const removeSuffixIfPresent = require("../promptParts/removeSuffixIfPresent") 8 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj") 9 | const cachePath = require("../promptParts/cachePath") 10 | 11 | //-------------------------------------------------- 12 | // Implementation 13 | //-------------------------------------------------- 14 | 15 | /** 16 | * Given the following code, convert from one format to another 17 | * @param {String} code to convert 18 | * @param {String} specObj, if not set the code will be treated as an ambigious snippet 19 | * @returns 20 | */ 21 | async function codeMinify( code, specObj = { name: "VariousSnippet", type: "snippet" } ) { 22 | 23 | // Get the various config values, with defaults 24 | // used to build the cacheKey 25 | const { type, name, cacheMode } = normalizeSpecObj(specObj); 26 | 27 | // Textual description in block wrapper 28 | const codeBlock = blockWrapper(`${type} code`, code); 29 | 30 | // The prompt to use 31 | let fullPrompt = [ 32 | `Convert the following ${type} code`, 33 | "", 34 | codeBlock, 35 | "", 36 | `To its minified code version, without any comments. Including class, parameter, todo or method comments.`, 37 | "", 38 | "```", 39 | "" 40 | ].join("\n") 41 | 42 | // Generate it 43 | let resp = await cachedCompletion(fullPrompt, cachePath( specObj, "CodeMinify" )); 44 | 45 | // Remove the ending "```" 46 | return removeSuffixIfPresent(resp, "```"); 47 | } 48 | 49 | module.exports = codeMinify; -------------------------------------------------------------------------------- /src/codegen/prototype/methodSpecToCodeSet.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../../openai/cachedCompletion"); 6 | const blockWrapper = require("../promptParts/blockWrapper") 7 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj") 8 | const removeSuffixIfPresent = require("../promptParts/removeSuffixIfPresent") 9 | const fullSpecPrefix = require("../promptParts/fullSpecPrefix") 10 | const cachePath = require("../promptParts/cachePath") 11 | 12 | // Various sequential steps 13 | const specToCodeClassSkeleton = require("./specToCodeClassSkeleton"); 14 | const codeMinify = require("./codeMinify"); 15 | const specToMinifiedBaseCode = require("./specToMinifiedBaseCode"); 16 | 17 | //-------------------------------------------------- 18 | // Implementation 19 | //-------------------------------------------------- 20 | 21 | /** 22 | * Given the class config, and the method set, 23 | * return the method code implementation, 24 | * and the depencies that would need to be included 25 | * 26 | * Note that this depends heavily on multiple substeps 27 | * 28 | * @param {Object} classConfig 29 | * @param {String} methodName 30 | * @param {String} methodSpec 31 | * 32 | * @return Generated code string, wrapped in a promise 33 | */ 34 | async function methodSpecToCodeSet( classConfig, methodName, methodSpec = "" ) { 35 | // Get the various config values, with defaults 36 | const { type, _humanLabel, _assistantLabel } = normalizeSpecObj(classConfig); 37 | 38 | // Lets do the big sequence, hopefully the cache's hold up 39 | const minifiedBaseCode = await specToMinifiedBaseCode( classConfig ); 40 | 41 | // Common prompt prefix 42 | const prefix_prompt = [ 43 | fullSpecPrefix( classConfig ), 44 | "", 45 | blockWrapper("The following is the incomplete minified code, of the current incomplete implementation", minifiedBaseCode), 46 | "", 47 | (methodSpec.length > 0)? blockWrapper(`The following is the textual description for the '${methodName}' method (or methods if overloaded)`, methodSpec) : "" 48 | ] 49 | 50 | // The first round prompt to use - to do the first draft as minified code 51 | // this multi step process is intentional, as it improves the overall code quality, 52 | // via encouraging the AI to perform "chain of thought" in the process. 53 | const g1_prompt = [ 54 | prefix_prompt, 55 | "", 56 | `${_humanLabel}:`, 57 | `Please generate the ${type} code for the '${methodName}' method, minified, without comments`, 58 | "", 59 | `${_assistantLabel}:`, 60 | "```", 61 | "" 62 | ].join("\n"); 63 | 64 | // Generate the generation 1 code 65 | const g1_codegen = removeSuffixIfPresent( await cachedCompletion(g1_prompt, cachePath(classConfig, "MethodCodeG1")), "```"); 66 | 67 | // Ok we got the G1 code, now lets do G2 68 | const g2_prompt = [ 69 | // Since g1 prompt already ends with \n 70 | g1_prompt + g1_codegen, 71 | "```", 72 | "", 73 | `${_humanLabel}:`, 74 | `Please improve the code. Improve readability. Fix any potential bugs, and handle edge cases. While simplifying and reducing the number of code steps and lines involved if possible.`, 75 | "", 76 | `${_assistantLabel}:`, 77 | "```", 78 | "" 79 | ].join("\n") 80 | 81 | // Generate the generation 2 code 82 | const g2_codegen = removeSuffixIfPresent( await cachedCompletion(g2_prompt, cachePath(classConfig, "MethodCodeG2")), "```"); 83 | 84 | // Ok we got the G2 code, now lets do G3 85 | const g3_prompt = [ 86 | prefix_prompt, 87 | "", 88 | `${_humanLabel}:`, 89 | `Please generate the ${type} code for the '${methodName}' method`, 90 | "", 91 | `${_assistantLabel}:`, 92 | "```", 93 | g2_codegen, 94 | "```", 95 | "", 96 | `${_humanLabel}:`, 97 | `Please improve the code, and add code comments, including detailed documentation comment for the method.`, 98 | "While simplifying and reducing the number of code steps and lines involved if possible.", 99 | "Avoid any code comment inside the method, if the comment is redundant.", 100 | "", 101 | `${_assistantLabel}:`, 102 | "```", 103 | "" 104 | ].join("\n") 105 | 106 | // Generate the generation 2 code 107 | const g3_codegen = removeSuffixIfPresent( await cachedCompletion(g3_prompt, cachePath(classConfig, "MethodCodeG3")), "```"); 108 | 109 | // Ok, lets get the dependency statements fetching 110 | const dep_prompt = [ 111 | prefix_prompt, 112 | "", 113 | `${_humanLabel}:`, 114 | `Please generate the ${type} code for the '${methodName}' method, with detailed documentations and comments`, 115 | "", 116 | g3_codegen, 117 | "```", 118 | "", 119 | `${_humanLabel}:`, 120 | `Please list any required package or namespace dependencies used by the '${methodName}' method, as import statements, without comments.`, 121 | `Do not include dependencies, if it was not used inside the '${methodName}' method, or already declared in the code skeleton.`, 122 | `You may return NO_DEPENDENCIES if there is no additional dependencies`, 123 | "", 124 | `${_assistantLabel}:`, 125 | "```", 126 | "" 127 | ].join("\n") 128 | // Generate the generation 2 code 129 | let dep_codegen = removeSuffixIfPresent( await cachedCompletion(dep_prompt, cachePath(classConfig, "MethodCodeDep")), "```").trim(); 130 | if( dep_codegen == "NO_DEPENDENCIES" ) { 131 | dep_codegen = ""; 132 | } 133 | 134 | // Return it as a pair 135 | return { 136 | codegen: g3_codegen, 137 | dependencies: dep_codegen 138 | } 139 | } 140 | module.exports = methodSpecToCodeSet; -------------------------------------------------------------------------------- /src/codegen/prototype/specExtraction.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../openai/cachedCompletion"); 6 | const normalizeSpecObj = require("./promptParts/normalizeSpecObj") 7 | const removeSuffixIfPresent = require("./promptParts/removeSuffixIfPresent") 8 | const fullSpecPrefix = require("./promptParts/fullSpecPrefix") 9 | const cachePath = require("./promptParts/cachePath") 10 | 11 | //-------------------------------------------------- 12 | // Implementation 13 | //-------------------------------------------------- 14 | 15 | /** 16 | * Given the following parameter in the specObj 17 | * 18 | * @param {Object} specObj to use 19 | * @param {String} instruction to execute using the spec 20 | * @param {String} cacheCategory to use for jsonl caching 21 | * 22 | * @return Generated code string, wrapped in a promise 23 | */ 24 | async function specExtraction( specObj, instruction, cacheCategory = "SpecExtraction", replyWithinQuote = true ) { 25 | // Get the various config values, with defaults 26 | const { _humanLabel, _assistantLabel } = normalizeSpecObj(specObj); 27 | 28 | // The prompt to use 29 | let fullPrompt = [ 30 | fullSpecPrefix( specObj ), 31 | "", 32 | `${_humanLabel}:`, 33 | `${instruction}`, 34 | "", 35 | `${_assistantLabel}:`, 36 | (replyWithinQuote? "```\n":""), 37 | ].join("\n") 38 | 39 | // Generate it 40 | let rep = await cachedCompletion(fullPrompt, cachePath(specObj, cacheCategory)); 41 | if( replyWithinQuote ) { 42 | return removeSuffixIfPresent(rep, "```"); 43 | } else { 44 | return rep; 45 | } 46 | } 47 | module.exports = specExtraction; -------------------------------------------------------------------------------- /src/codegen/prototype/specQuestionAndAnswer.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../../openai/cachedCompletion"); 6 | const blockWrapper = require("../promptParts/blockWrapper") 7 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj") 8 | const removeSuffixIfPresent = require("../promptParts/removeSuffixIfPresent") 9 | const fullSpecPrefix = require("../promptParts/fullSpecPrefix") 10 | const cachePath = require("../promptParts/cachePath") 11 | 12 | //-------------------------------------------------- 13 | // Implementation 14 | //-------------------------------------------------- 15 | 16 | /** 17 | * Given the following parameter in the config 18 | * 19 | * @param {Object} config 20 | * @param {String} question, which is wrapped inside a block (avoid contextual confusion) 21 | * 22 | * @return Generate an answer from the given question 23 | */ 24 | async function specQuestionAndAnswer( config, question ) { 25 | 26 | // Get the various config values, with defaults 27 | const { type, spec, _humanLabel, _assistantLabel } = normalizeSpecObj(config); 28 | 29 | // Textual description in block wrapper 30 | const questionBlock = blockWrapper("Question", question); 31 | 32 | // The prompt to use 33 | let fullPrompt = [ 34 | fullSpecPrefix( config ), 35 | "", 36 | `${_humanLabel}:`, 37 | `Please answer the question in the following block`, 38 | "", 39 | questionBlock, 40 | "", 41 | `${_assistantLabel}:`, 42 | "" 43 | ].join("\n") 44 | 45 | // Generate and return it 46 | return await cachedCompletion(fullPrompt, cachePath(config, "SpecQuestionAndAnswer")); 47 | } 48 | module.exports = specQuestionAndAnswer; -------------------------------------------------------------------------------- /src/codegen/prototype/specToClassCommentBlock.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../../openai/cachedCompletion"); 6 | const blockWrapper = require("../promptParts/blockWrapper") 7 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj") 8 | const removeSuffixIfPresent = require("../promptParts/removeSuffixIfPresent") 9 | const cachePath = require("../promptParts/cachePath"); 10 | const specToCodeClassSkeleton = require("./specToCodeClassSkeleton"); 11 | const methodSpecToCodeSet = require("./methodSpecToCodeSet"); 12 | const codeExtraction = require("./codeExtraction"); 13 | const codeConversion = require("./codeConversion"); 14 | const indentString = require("../../util/indentString"); 15 | const specToMinifiedBaseCode = require("./specToMinifiedBaseCode"); 16 | const fullSpecPrefix = require("../promptParts/fullSpecPrefix"); 17 | 18 | //-------------------------------------------------- 19 | // Implementation 20 | //-------------------------------------------------- 21 | 22 | /** 23 | * Given the class spec, and method spec. Generate the class comment block 24 | * 25 | * @param {Object} classSpecObj object representing the classSpec 26 | * 27 | * @return Fully generated code 28 | */ 29 | async function specToClassCommentBlock( classSpecObj ) { 30 | // Get the various config values, with defaults 31 | const { name, type, _humanLabel, _assistantLabel } = normalizeSpecObj( classSpecObj ); 32 | 33 | // We need the class spec prefix (to use later) 34 | const classPrefix = fullSpecPrefix( classSpecObj ); 35 | 36 | // Lets get the minified base code 37 | const minifiedBaseCode = await specToMinifiedBaseCode( classSpecObj ); 38 | 39 | // Because the "spec" can grow to be too excessively verbose, 40 | // we perform a comment first pass, using the minified code base 41 | const g1_prompt = [ 42 | `${_humanLabel}:`, 43 | `For the following ${type} minified code`, 44 | "", 45 | blockWrapper(`${type} code`, minifiedBaseCode), 46 | "", 47 | `Please generate the ${name} class top level comment, as a comment block like the following`, 48 | "", 49 | "```", 50 | "/**", 51 | " * Detailed class comment here", 52 | " */", 53 | "```", 54 | "", 55 | "Return only the comment block.", 56 | "", 57 | `${_assistantLabel}:`, 58 | "```", 59 | "" 60 | ].join("\n"); 61 | 62 | // Generate it 63 | const g1_result = removeSuffixIfPresent( await cachedCompletion(g1_prompt, cachePath( classSpecObj, "SpecClassCommentG1")), "```"); 64 | 65 | // Lets perform a g2 pass 66 | const g2_prompt = [ 67 | classPrefix, 68 | "", 69 | `The following is the comment block segment for the ${name} class / interface`, 70 | "", 71 | "```", 72 | g1_result, 73 | "```", 74 | "", 75 | `${_humanLabel}:`, 76 | `Improve the ${name} comment block.`, 77 | "To better explain the class/interface purpose, and how it should be used.", 78 | "While improving the overall readability, grammer and spelling in the process.", 79 | "", 80 | "Focus only on the summary, avoid adding uneeded implementation notes, or method/property/constructor specific details", 81 | `Include explaination on how to use the ${name}, such as its use cases, and its usage flows, when possible.`, 82 | "", 83 | "Do not include details of the properties, constructor and methods, as that will be documented seperately", 84 | "Do not include the implementaiton notes in the class top level comments.", 85 | "", 86 | "Return only the comment block.", 87 | "", 88 | `${_assistantLabel}:`, 89 | "```", 90 | "" 91 | ].join("\n"); 92 | 93 | // Generate it 94 | const g2_result = removeSuffixIfPresent( await cachedCompletion(g2_prompt, cachePath( classSpecObj, "SpecClassCommentG2")), "```"); 95 | 96 | // Lets perform a g2 pass 97 | const g3_prompt = [ 98 | `The following is the comment block segment for the ${name} class / interface`, 99 | "", 100 | "```", 101 | g2_result, 102 | "```", 103 | "", 104 | `${_humanLabel}:`, 105 | `Please rewrite the ${name} comment block, for improved readability.`, 106 | // `If explaination is done for a sequence of steps please ensure it is done in point form`, 107 | "", 108 | "Return only the comment block.", 109 | "", 110 | `${_assistantLabel}:`, 111 | "```", 112 | "" 113 | ].join("\n"); 114 | 115 | // Generate it 116 | const g3_result = removeSuffixIfPresent( await cachedCompletion(g3_prompt, cachePath( classSpecObj, "SpecClassCommentG3")), "```"); 117 | 118 | // Return it 119 | return g3_result; 120 | } 121 | module.exports = specToClassCommentBlock; -------------------------------------------------------------------------------- /src/codegen/prototype/specToClassPropertyWithComments.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../../openai/cachedCompletion"); 6 | const blockWrapper = require("../promptParts/blockWrapper") 7 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj") 8 | const removeSuffixIfPresent = require("../promptParts/removeSuffixIfPresent") 9 | const cachePath = require("../promptParts/cachePath"); 10 | const specToCodeClassSkeleton = require("./specToCodeClassSkeleton"); 11 | const methodSpecToCodeSet = require("./methodSpecToCodeSet"); 12 | const codeExtraction = require("./codeExtraction"); 13 | const codeConversion = require("./codeConversion"); 14 | const indentString = require("../../util/indentString"); 15 | const specToMinifiedBaseCode = require("./specToMinifiedBaseCode"); 16 | const fullSpecPrefix = require("../promptParts/fullSpecPrefix"); 17 | 18 | //-------------------------------------------------- 19 | // Implementation 20 | //-------------------------------------------------- 21 | 22 | /** 23 | * Given the class spec, and method spec. Generate the class comment block 24 | * 25 | * @param {Object} classSpecObj object representing the classSpec 26 | * 27 | * @return Fully generated code 28 | */ 29 | async function specToClassPropertyWithcomments( classSpecObj ) { 30 | // Get the various config values, with defaults 31 | const { name, type, _humanLabel, _assistantLabel } = normalizeSpecObj( classSpecObj ); 32 | 33 | // We need the class spec prefix (to use later) 34 | const classPrefix = fullSpecPrefix( classSpecObj ); 35 | 36 | // Lets get the minified base code 37 | const minifiedBaseCode = await specToMinifiedBaseCode( classSpecObj ); 38 | 39 | // Get the property declaration 40 | let propertyDeclaration = await codeExtraction(minifiedBaseCode, `${type} code`, "all property declarations"); 41 | 42 | // Lets perform a g2 pass 43 | const fullPrompt = [ 44 | classPrefix, 45 | "", 46 | `The following is the various property declaration for the ${name} class / interface`, 47 | "", 48 | "```", 49 | propertyDeclaration, 50 | "```", 51 | "", 52 | `${_humanLabel}:`, 53 | `Add comments to the ${name} property declarations.`, 54 | "Return the property declarations with comments.", 55 | "", 56 | `${_assistantLabel}:`, 57 | "```", 58 | "" 59 | ].join("\n"); 60 | 61 | // Generate it 62 | return removeSuffixIfPresent( await cachedCompletion(fullPrompt, cachePath( classSpecObj, "SpecClassProperty")), "```"); 63 | } 64 | module.exports = specToClassPropertyWithcomments; -------------------------------------------------------------------------------- /src/codegen/prototype/specToCodeClassSkeleton.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../../openai/cachedCompletion"); 6 | const blockWrapper = require("../promptParts/blockWrapper") 7 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj") 8 | const removeSuffixIfPresent = require("../promptParts/removeSuffixIfPresent") 9 | const fullSpecPrefix = require("../promptParts/fullSpecPrefix") 10 | const cachePath = require("../promptParts/cachePath") 11 | 12 | //-------------------------------------------------- 13 | // Implementation 14 | //-------------------------------------------------- 15 | 16 | /** 17 | * Given the following parameter in the config 18 | * 19 | * - type : Programming language (eg. java, javascript) 20 | * - name : Name of the class to generate 21 | * - namespace : package namespace of the class 22 | * - spec : Detailed textual description describing the class 23 | * 24 | * @param {Object} config 25 | * 26 | * @return Generated code string, wrapped in a promise 27 | */ 28 | async function specToCodeClassSkeleton( config ) { 29 | 30 | // Get the various config values, with defaults 31 | const { type, _humanLabel, _assistantLabel } = normalizeSpecObj(config); 32 | 33 | // The prompt to use 34 | let fullPrompt = [ 35 | fullSpecPrefix( config ), 36 | "", 37 | `${_humanLabel}:`, 38 | `Please generate the ${type} code skeleton with detailed class comments, and all required dependencies, and properties. Without any of the actual constructor or function implementations`, 39 | "", 40 | `${_assistantLabel}:`, 41 | "```", 42 | "" 43 | ].join("\n") 44 | 45 | // Generate it 46 | let codegen = await cachedCompletion(fullPrompt, cachePath(config, "ClassSkeleton")); 47 | 48 | 49 | // `Please generate the ${type} code skeleton with comments, with any required dependencies, and properties, without any of the actual constructor or function implementations.`, 50 | // `Include detailed class comments, rewritten for clarity and readability.`, 51 | 52 | // Remove the ending "```" 53 | return removeSuffixIfPresent(codegen, "```") 54 | 55 | // // Or throw an error (and warning) accordingly 56 | // console.warn([ 57 | // "## ERROR DETAILS :: code gen output", 58 | // codegen 59 | // ].join("\n")); 60 | // throw "Unexpected INCOMPLETE codegen without markdown code block closure (see warning logs for its output)" 61 | } 62 | module.exports = specToCodeClassSkeleton; -------------------------------------------------------------------------------- /src/codegen/prototype/specToFullClassCode.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../../openai/cachedCompletion"); 6 | const blockWrapper = require("../promptParts/blockWrapper") 7 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj") 8 | const removeSuffixIfPresent = require("../promptParts/removeSuffixIfPresent") 9 | const fullSpecPrefix = require("../promptParts/fullSpecPrefix") 10 | const cachePath = require("../promptParts/cachePath"); 11 | const specToCodeClassSkeleton = require("./specToCodeClassSkeleton"); 12 | const methodSpecToCodeSet = require("./methodSpecToCodeSet"); 13 | const codeExtraction = require("./codeExtraction"); 14 | const codeConversion = require("./codeConversion"); 15 | const indentString = require("../../util/indentString"); 16 | const specToMinifiedBaseCode = require("./specToMinifiedBaseCode"); 17 | const specToClassCommentBlock = require("./specToClassCommentBlock"); 18 | const specToClassPropertyWithcomments = require("./specToClassPropertyWithComments"); 19 | 20 | //-------------------------------------------------- 21 | // Implementation 22 | //-------------------------------------------------- 23 | 24 | /** 25 | * Given the class spec, and method spec. Generate the full code 26 | * 27 | * @param {Object} classSpecObj object representing the classSpec 28 | * @param {Object} methodSpecMap an object map of individual methodSpec's 29 | * 30 | * @return Fully generated code 31 | */ 32 | async function specToFullClassCode( classSpecObj, methodSpecMap = {} ) { 33 | // Get the various config values, with defaults 34 | const { name, type, _humanLabel, _assistantLabel } = normalizeSpecObj( classSpecObj ); 35 | 36 | // Lets get the minified base code 37 | const minifiedBaseCode = await specToMinifiedBaseCode( classSpecObj ); 38 | 39 | // Method list 40 | let methodList = JSON.parse( await codeExtraction(minifiedBaseCode, `${type} code`, "list of method names (including the constructor), without the parameters or brackets. As a JSON array. If there is a class constructor, return it as the first item", classSpecObj) ); 41 | 42 | // Method codegen set map 43 | let methodCodeSetMap = {}; 44 | 45 | // If method name matches class name, rewrite it as "constructor" 46 | for( let i=0; i= 0; 53 | 54 | // For each method, 55 | // Lets compute the detailed implementtation of each component with comments 56 | for( let methodName of methodList ) { 57 | // We get the methodSpec with a safety check, as the method could be using a reserved keyword, and not have a spec defined 58 | let methodSpec = methodSpecMap[methodName] || ""; 59 | if( !(typeof methodSpec === 'string' || methodSpec instanceof String) ) { 60 | methodSpec = ""; 61 | } 62 | 63 | // Use the spec 64 | methodCodeSetMap[ methodName ] = await methodSpecToCodeSet( classSpecObj, methodName, methodSpec ); 65 | } 66 | 67 | // Segment extraction 68 | let packageNamespaceAndDependecies = await codeExtraction(minifiedBaseCode, `${type} code`, "Package namespace (if any), and all the dependencies declarations (if any), return only as package and import statements"); 69 | let openingClassDeclaration = await codeExtraction(minifiedBaseCode, `${type} code`, "class declaration line, excluding the opening bracket '{', excluding dependencies, excluding methods and properties, excluding closing bracket '}'"); 70 | let classBlockComment = await specToClassCommentBlock(classSpecObj); 71 | let propertyDeclarationWithComments = await specToClassPropertyWithcomments(classSpecObj); 72 | 73 | // Lets merge the dependencies 74 | for( const methodName of methodList ) { 75 | packageNamespaceAndDependecies += "\n" + methodCodeSetMap[ methodName ].dependencies; 76 | } 77 | 78 | // Lets remove duplicates & lets start building the full code toget 79 | let fullCode = await codeExtraction( 80 | packageNamespaceAndDependecies, 81 | `${type} snippet code, containing package namespace (if any) and dependencies declaration`, 82 | "Package namespace (if any), and all the dependencies declarations, with any duplicates removed, and redundant blank lines removed.", 83 | classSpecObj 84 | ); 85 | 86 | // Lets add in the opening 87 | fullCode += "\n\n"+classBlockComment; 88 | fullCode += "\n"+openingClassDeclaration + " {\n"; 89 | 90 | // The various properties 91 | if( propertyDeclarationWithComments.trim().length > 1 ) { 92 | fullCode += "\n //" 93 | fullCode += "\n // Properties" 94 | fullCode += "\n //\n" 95 | fullCode += "\n" + indentString(propertyDeclarationWithComments, 1, " ") + "\n"; 96 | } 97 | 98 | // And the constructor 99 | if( methodList.length > 0 ) { 100 | 101 | if( hasConstructor ) { 102 | fullCode += "\n //" 103 | fullCode += "\n // Constructor" 104 | fullCode += "\n //\n" 105 | fullCode += "\n" + indentString(methodCodeSetMap[ "constructor" ].codegen, 1, " ") + "\n"; 106 | } 107 | 108 | fullCode += "\n //" 109 | fullCode += "\n // Method implementation" 110 | fullCode += "\n //\n" 111 | 112 | // Lets add each part of the methods 113 | for( const methodName of methodList ) { 114 | if( methodName == "constructor" ) { 115 | continue; 116 | } 117 | fullCode += "\n" + indentString(methodCodeSetMap[ methodName ].codegen, 1, " ") + "\n"; 118 | } 119 | 120 | } 121 | 122 | // Add the closing bracket 123 | fullCode += "\n"+"}"; 124 | 125 | // Return the full code 126 | return fullCode; 127 | } 128 | module.exports = specToFullClassCode; -------------------------------------------------------------------------------- /src/codegen/prototype/specToMinifiedBaseCode.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../../openai/cachedCompletion"); 6 | const blockWrapper = require("../promptParts/blockWrapper") 7 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj") 8 | const removeSuffixIfPresent = require("../promptParts/removeSuffixIfPresent") 9 | const fullSpecPrefix = require("../promptParts/fullSpecPrefix") 10 | const cachePath = require("../promptParts/cachePath"); 11 | const codeMinify = require("./codeMinify"); 12 | const getTokenCount = require("../../openai/getTokenCount"); 13 | 14 | //-------------------------------------------------- 15 | // Implementation 16 | //-------------------------------------------------- 17 | 18 | /** 19 | * Given the following parameter in the config 20 | * 21 | * - type : Programming language (eg. java, javascript) 22 | * - name : Name of the class to generate 23 | * - namespace : package namespace of the class 24 | * - spec : Detailed textual description describing the class 25 | * 26 | * @param {Object} config 27 | * 28 | * @return Generated code string, wrapped in a promise 29 | */ 30 | async function specToMinifiedBaseCode( classSpec ) { 31 | 32 | // Get the various config values, with defaults 33 | const { name, type, _humanLabel, _assistantLabel, _pLang } = normalizeSpecObj(classSpec); 34 | 35 | // We intentionally generate the code through multiple rounds. 36 | // As it improves the overall code quality, via encouraging the 37 | // AI to perform "chain of thought" in the process. 38 | // ---- 39 | 40 | // The prompt to use 41 | let g1_prompt = [ 42 | fullSpecPrefix( classSpec ), 43 | "", 44 | `${_humanLabel}:`, 45 | `Please generate the ${_pLang} code, with the minimum implementation according the spec. Minified without any comments, or code indentation.`, 46 | "", 47 | `${_assistantLabel}:`, 48 | "```", 49 | "" 50 | ].join("\n") 51 | 52 | // Generate it 53 | let g1_codegen = removeSuffixIfPresent(await cachedCompletion(g1_prompt, cachePath(classSpec, "MinifiedBaseCodeG1")), "```"); 54 | 55 | // Token length safety 56 | if( getTokenCount(g1_codegen) > 2000 ) { 57 | throw "[[Token Limit Error]] Gen 1 draft of the unminified base code, exceeds a 2,000 tokens - consider reducing and splitting the class : "+name; 58 | } 59 | 60 | // Unfortunately, there is a non zero chance that the code output is not 100% minified, so we need to do a minify pass 61 | let g1_minified = await codeMinify(g1_codegen, classSpec); 62 | 63 | // Token length safety 64 | if( getTokenCount(g1_minified) > 1500 ) { 65 | throw "[[Token Limit Error]] Gen 1 draft of the minified base code, exceeds a 1,000 tokens - consider reducing and splitting the class : "+name; 66 | } 67 | 68 | // Lets do a second pass 69 | let g2_prompt = [ 70 | g1_prompt+g1_minified, 71 | "```", 72 | "", 73 | `${_humanLabel}:`, 74 | `Please improve the ${type} code, completing any missing implementation that is requied. And fixing any obvious errors.`, 75 | `Keep the output as minified ${type} code, without spaces, or code indentation.`, 76 | "", 77 | `${_assistantLabel}:`, 78 | "```", 79 | "" 80 | ].join("\n") 81 | 82 | // Generate it 83 | let g2_codegen = removeSuffixIfPresent(await cachedCompletion(g2_prompt, cachePath(classSpec, "MinifiedBaseCodeG2")), "```"); 84 | 85 | // Token length safety 86 | if( getTokenCount(g2_codegen) > 2000 ) { 87 | throw "[[Token Limit Error]] Gen 2 draft of the unminified base code, exceeds a 2,000 tokens - consider reducing and splitting the class : "+name; 88 | } 89 | 90 | // Unfortunately (again), there is a non zero chance that the code output is not 100% minified, so we need to do a minify pass 91 | let g2_minified = await codeMinify(g2_codegen, classSpec); 92 | 93 | // Token length safety 94 | if( getTokenCount(g2_minified) > 1500 ) { 95 | throw "[[Token Limit Error]] Gen 2 draft of the minified base code, exceeds a 1,000 tokens - consider reducing and splitting the class : "+name; 96 | } 97 | return g2_minified; 98 | } 99 | module.exports = specToMinifiedBaseCode; -------------------------------------------------------------------------------- /src/codegen/segment/specChangeSuggestion.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj") 6 | const cachedCompletion = require("../../openai/cachedCompletion") 7 | const fullSpecPrefix = require("../promptParts/fullSpecPrefix") 8 | const cachePath = require("../promptParts/cachePath") 9 | 10 | //-------------------------------------------------- 11 | // Implementation 12 | //-------------------------------------------------- 13 | 14 | /** 15 | * Given the following parameter in the config 16 | * 17 | * - type : Programming language (eg. java, javascript) 18 | * - name : Name of the class to generate 19 | * - namespace : package namespace of the class 20 | * - spec : Detailed textual description describing the class 21 | * 22 | * @param {Object} config 23 | * 24 | * @return Generate the suggested changes to be done by the assistant 25 | */ 26 | async function specChangeSuggestion( config ) { 27 | // Get the various config values, with defaults 28 | const { _humanLabel, _assistantLabel } = normalizeSpecObj(config); 29 | 30 | // The prompt to use 31 | let fullPrompt = [ 32 | fullSpecPrefix( config ), 33 | "", 34 | `${_humanLabel}:`, 35 | `Please suggest changes, to improve the specification. In point form.`, 36 | `Reply with NO_SUGGESTION if there is no suggested changes, to improve the specification.`, 37 | "", 38 | `${_assistantLabel}:`, 39 | "" 40 | ].join("\n") 41 | 42 | // Generate and return the response 43 | return await cachedCompletion(fullPrompt, cachePath(config, `SpecChangeSuggestion`)); 44 | } 45 | 46 | module.exports = specChangeSuggestion; -------------------------------------------------------------------------------- /src/codegen/segment/specChangeUsingFeedback.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../../openai/cachedCompletion") 6 | const blockWrapper = require("../promptParts/blockWrapper") 7 | const removeSuffixIfPresent = require("../promptParts/removeSuffixIfPresent") 8 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj") 9 | const fullSpecPrefix = require("../promptParts/fullSpecPrefix") 10 | const cachePath = require("../promptParts/cachePath") 11 | 12 | //-------------------------------------------------- 13 | // Implementation 14 | //-------------------------------------------------- 15 | 16 | /** 17 | * Given the following parameter in the config 18 | * 19 | * - type : Programming language (eg. java, javascript) 20 | * - name : Name of the class to generate 21 | * - namespace : package namespace of the class 22 | * - spec : Detailed textual description describing the class 23 | * 24 | * @param {Object} config 25 | * 26 | * @return Generate the suggested changes to be done by the assistant 27 | */ 28 | async function specChangeUsingFeedback( config, assistantSuggestion, feedback ) { 29 | 30 | // Throw on empty feedback 31 | if( feedback == null || (feedback = feedback.trim()) == "" ) { 32 | throw "Unexpected empty feedback to apply" 33 | } 34 | 35 | // Get the various config values, with defaults 36 | const { spec, _humanLabel, _assistantLabel } = normalizeSpecObj(config); 37 | 38 | // Textual description in block wrapper 39 | const desc = blockWrapper("Textual description", spec); 40 | 41 | // Assistant suggestion block 42 | let assistantSuggestionBlock = ""; 43 | if( assistantSuggestion && (assistantSuggestion = assistantSuggestion.trim()) != "") { 44 | assistantSuggestionBlock = [ 45 | `Please suggest changes which can be made by you, as an AI assistant, to improve the textual description. In point form.`, 46 | `Along with seperate set of changes I as the programmer should make to improve the textual description. In point form.`, 47 | "Note that implementation specific details of individual method/functions, will be described in a seperate document, and is not needed in the class textual description.", 48 | `Reply with NO_SUGGESTION if there is no suggested changes the assistant can perform`, 49 | "", 50 | `${_assistantLabel}:`, 51 | assistantSuggestion, 52 | "", 53 | `${_humanLabel}:`, 54 | ].join("\n"); 55 | } 56 | 57 | // Textual description in block wrapper 58 | const feedbackBlock = blockWrapper("Feedback", feedback); 59 | 60 | // The prompt to use 61 | let fullPrompt = [ 62 | fullSpecPrefix( config ), 63 | "", 64 | `${_humanLabel}:`, 65 | assistantSuggestionBlock, 66 | "Make changes and return the textual description, based on the following feedback.", 67 | "", 68 | feedbackBlock, 69 | "", 70 | `${_assistantLabel}:`, 71 | "```", 72 | "" 73 | ].join("\n") 74 | 75 | // Generate it 76 | let resp = await cachedCompletion(fullPrompt, cachePath( config, "SpecChangeUsingFeedback")); 77 | 78 | // Remove the ending "```" 79 | return removeSuffixIfPresent(resp, "```") 80 | 81 | // // Or throw an error (and warning) accordingly 82 | // console.warn([ 83 | // "## ERROR DETAILS :: incomplete textual description block", 84 | // resp 85 | // ].join("\n")); 86 | // throw "Unexpected INCOMPLETE textual description changes (see warning logs for its output)" 87 | } 88 | 89 | module.exports = specChangeUsingFeedback; -------------------------------------------------------------------------------- /src/codegen/segment/specExtraction.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../../openai/cachedCompletion"); 6 | const normalizeSpecObj = require("../promptParts/normalizeSpecObj") 7 | const removeSuffixIfPresent = require("../promptParts/removeSuffixIfPresent") 8 | const fullSpecPrefix = require("../promptParts/fullSpecPrefix") 9 | const cachePath = require("../promptParts/cachePath") 10 | 11 | //-------------------------------------------------- 12 | // Implementation 13 | //-------------------------------------------------- 14 | 15 | /** 16 | * Given the following parameter in the specObj 17 | * Extract the relevent result information, with the provided instruction 18 | * 19 | * @param {Object} specObj to use 20 | * @param {String} instruction to execute using the spec 21 | * @param {String} cacheCategory to use for jsonl caching 22 | * @param {Boolean} replyWithinQuote, indicates if the result should be wrapped within a "```" quote block 23 | * @return Generated code string, wrapped in a promise 24 | */ 25 | async function specExtraction( specObj, instruction, cacheCategory = "SpecExtraction", replyWithinQuote = true ) { 26 | // Get the various config values, with defaults 27 | const { _humanLabel, _assistantLabel } = normalizeSpecObj(specObj); 28 | 29 | // The prompt to use 30 | let fullPrompt = [ 31 | fullSpecPrefix( specObj ), 32 | "", 33 | `${_humanLabel}:`, 34 | `${instruction}`, 35 | "", 36 | `${_assistantLabel}:`, 37 | (replyWithinQuote? "```\n":""), 38 | ].join("\n") 39 | 40 | // Generate it 41 | let rep = await cachedCompletion(fullPrompt, cachePath(specObj, cacheCategory)); 42 | if( replyWithinQuote ) { 43 | return removeSuffixIfPresent(rep, "```"); 44 | } else { 45 | return rep; 46 | } 47 | } 48 | module.exports = specExtraction; -------------------------------------------------------------------------------- /src/core/config.js: -------------------------------------------------------------------------------- 1 | const path = require("path") 2 | const ConfigLoader = require("@js-util/config-loader") 3 | 4 | // 5 | // Load the various config files (if found), 6 | // along with the various default value fallbacks 7 | // 8 | const cwd = process.cwd(); 9 | const config = new ConfigLoader({ 10 | fileList: [ 11 | path.resolve(cwd,"./config.json"), 12 | path.resolve(cwd,"./config.jsonc"), 13 | "./config.json", 14 | "./config.jsonc", 15 | 16 | path.resolve(cwd,"./english-compiler.json"), 17 | path.resolve(cwd,"./english-compiler.jsonc"), 18 | "./english-compiler.json", 19 | "./english-compiler.jsonc" 20 | ], 21 | default: { 22 | 23 | // Openai API key 24 | "openai_apikey": null, 25 | 26 | // Prompt caching dir, this can also be used to build up a historical dataset 27 | "prompt_cache_dir": null, 28 | 29 | // Specification directory 30 | "spec_dir": "./spec", 31 | 32 | // Source code output directory 33 | "code_dir": "./src", 34 | 35 | // Test code output directory 36 | "test_dir": "./test", 37 | 38 | // Add some personality based remarks 39 | "personality": false 40 | } 41 | }); 42 | 43 | // Export the config 44 | module.exports = config; -------------------------------------------------------------------------------- /src/debug/promptAndAnswer.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const cachedCompletion = require("../openai/cachedCompletion"); 6 | 7 | //-------------------------------------------------- 8 | // Implmentation 9 | //-------------------------------------------------- 10 | 11 | /** 12 | * Simplified question and answer prompt interface 13 | * With a misc cache 14 | * 15 | * @param {String} prompt 16 | * @returns {String} 17 | */ 18 | async function promptAndAnswer( prompt ) { 19 | return await cachedCompletion( prompt, "misc/promptAndAnswer.jsonl" ) 20 | } 21 | 22 | // Export the module 23 | module.exports = promptAndAnswer; 24 | 25 | //-------------------------------------------------- 26 | // Direct execution 27 | //-------------------------------------------------- 28 | 29 | // Special direct execution handling 30 | // https://stackoverflow.com/questions/6398196/detect-if-called-through-require-or-directly-by-command-line 31 | if( require.main === module ) { 32 | (async function() { 33 | 34 | // Argument check 35 | const reqPrompt = process.argv[2]; 36 | if( reqPrompt == null ) { 37 | console.error( "[ERROR] Missing request prompt as first argument" ); 38 | console.error( `[ERROR] Example: node ${ process.argv[1] } "Explain large language models"` ); 39 | process.exit(1); 40 | } 41 | 42 | // Response 43 | const resp = await promptAndAnswer(reqPrompt); 44 | console.log(resp); 45 | })(); 46 | } -------------------------------------------------------------------------------- /src/openai/cachedCompletion.js: -------------------------------------------------------------------------------- 1 | // Dependencies setup 2 | //---------------------------- 3 | 4 | const fs = require("fs") 5 | const path = require("path") 6 | const jsonl = require("node-jsonl") 7 | const lockfile = require('proper-lockfile') 8 | 9 | const fileExist = require("../util/fileExist") 10 | const promptToCompletion = require("./promptToCompletion") 11 | 12 | // Config loading 13 | //---------------------------- 14 | const cacheDir = require("../core/config").prompt_cache_dir; 15 | 16 | // Function implementation 17 | //---------------------------- 18 | 19 | /** 20 | * Perform a prompt to completion, using the jsonl cache file if provided 21 | * 22 | * The downside of this API, is that it enforces both the stop character, model, and temprature (which is set as 0) 23 | * 24 | * @param {String} prompt 25 | * @param {String} jsonlCacheFile 26 | */ 27 | async function cachedCompletion(prompt, jsonlCacheFile = ".default-promptcache.jsonl", cacheMode = "public") { 28 | 29 | // Cache dir is not set, or cache file is not set, skip it 30 | if( cacheDir == null || jsonlCacheFile == null || cacheMode == "none" ) { 31 | return await promptToCompletion(prompt); 32 | } 33 | 34 | // Compute the full file path, and dir path 35 | let fullPath = null; 36 | if( cacheMode == "public" ) { 37 | fullPath = path.resolve( cacheDir, jsonlCacheFile ); 38 | } else { 39 | fullPath = path.resolve( cacheDir, ".private", jsonlCacheFile ); 40 | } 41 | const dirPath = path.dirname( fullPath ); 42 | 43 | // Ensure dir setup 44 | await fs.promises.mkdir( dirPath, { recursive: true }); 45 | 46 | // Scan the file, if it exists 47 | // this is done without file locking, as a performance speed up 48 | // for cache hit, at the cost of higher latency on cache miss 49 | // 50 | // additionally because it can cause read/write contention - it can fail. 51 | // as such any error here is ignored, as it will be retried with a file lock. 52 | if( await fileExist(fullPath) ) { 53 | try { 54 | // Scan the various jsonl lines 55 | const rl = jsonl.readlines(fullPath); 56 | while(true) { 57 | const {value, done} = await rl.next(); 58 | if(done) break; 59 | // Return the matching completion when found 60 | if( value.prompt == prompt ) { 61 | return value.completion; 62 | } 63 | } 64 | } catch(e) { 65 | // exception is ignored 66 | } 67 | } 68 | 69 | // Get the lock 70 | let lockRelease = await lockfile.lock(fullPath, { realpath:false }); 71 | 72 | // Perform actions within a lock 73 | try { 74 | // Scan the file, if it exists 75 | if( await fileExist(fullPath) ) { 76 | // Scan the various jsonl lines 77 | const rl = jsonl.readlines(fullPath); 78 | while(true) { 79 | const {value, done} = await rl.next(); 80 | if(done) break; 81 | // Return the matching completion when found 82 | if( value.prompt == prompt ) { 83 | return value.completion; 84 | } 85 | } 86 | } 87 | 88 | // Match not found, perform the prompt 89 | const completion = await promptToCompletion(prompt); 90 | 91 | // Write it 92 | await fs.promises.appendFile(fullPath, JSON.stringify({ prompt:prompt, completion:completion })+"\n", { encoding:"utf8" }); 93 | 94 | // return the completion response 95 | return completion 96 | } finally { 97 | // Release the lock 98 | await lockRelease(); 99 | } 100 | } 101 | 102 | // Module export 103 | //---------------------------- 104 | module.exports = cachedCompletion; 105 | 106 | // Special direct execution handling 107 | // https://stackoverflow.com/questions/6398196/detect-if-called-through-require-or-directly-by-command-line 108 | //---------------------------- 109 | if( require.main === module ) { 110 | (async function() { 111 | 112 | // Argument check 113 | const reqPrompt = process.argv[2]; 114 | if( reqPrompt == null ) { 115 | console.error( "[ERROR] Missing request prompt as first argument" ); 116 | console.error( `[ERROR] Example: node ./cachedCompletion.js "tell me a joke?"` ); 117 | process.exit(1); 118 | } 119 | 120 | // Response 121 | const resp = await cachedCompletion(reqPrompt); 122 | console.log(resp); 123 | })(); 124 | } -------------------------------------------------------------------------------- /src/openai/getApiKey.js: -------------------------------------------------------------------------------- 1 | // Get the API key 2 | const openai_apikey = require("../core/config").openai_apikey; 3 | 4 | // Safety check 5 | if(openai_apikey == null) { 6 | throw "Missing valid `openai_apikey`, please ensure that the code-ai config.jsonc is setup with it" 7 | } 8 | 9 | // Export 10 | module.exports = openai_apikey; -------------------------------------------------------------------------------- /src/openai/getTokenCount.js: -------------------------------------------------------------------------------- 1 | // Initialize the tokenizer 2 | const GPT3Tokenizer = require('gpt3-tokenizer').default; 3 | const tokenizer = new GPT3Tokenizer({ type: 'gpt3' }); 4 | 5 | /** 6 | * Given the input data, encode and get the token count 7 | * @param {String} data 8 | * 9 | * @return token length as int 10 | */ 11 | module.exports = function getTokenCount( data ) { 12 | let tokenObj = tokenizer.encode( data ); 13 | return tokenObj.bpe.length; 14 | } -------------------------------------------------------------------------------- /src/openai/promptToCompletion.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The following script is used to ask a prompt, and return its response 3 | * 4 | * This does not perform any caching / saving, and can be imported, or executed directly 5 | * 6 | * ``` 7 | * node ./promptToCompletion.js "getting-started/Writing-your-first-test" 8 | * ``` 9 | **/ 10 | 11 | // Load dependency modules, and keys 12 | const fetch = require("node-fetch"); 13 | const openai_key = require("./getApiKey"); 14 | const GPT3Tokenizer = require('gpt3-tokenizer').default; 15 | 16 | // Default config settings to use 17 | const defaultConfig = { 18 | "model": "text-davinci-003", 19 | "temperature": 0, 20 | "max_tokens": "auto", 21 | "top_p": 1, 22 | "frequency_penalty": 0, 23 | "presence_penalty": 0, 24 | "best_of": 1, 25 | 26 | // Important note!: we split the endoftext token very 27 | // intentionally,to avoid causing issues when this file is parsed 28 | // by GPT-3 based AI. 29 | 30 | // Default stop keyword 31 | "stop": ["<|"+"endoftext"+"|>"], 32 | 33 | // Default prompt 34 | "prompt": "<|"+"endoftext"+"|>", 35 | 36 | // Return as a string if false, 37 | // else return the raw openAI API response 38 | "rawApi": false 39 | }; 40 | 41 | // Initialize the tokenizer 42 | const tokenizer = new GPT3Tokenizer({ type: 'gpt3' }); 43 | 44 | /** 45 | * Given the prompt config, return the API result 46 | * 47 | * @param {String | Object} inConfig, containing the prompt or other properties 48 | * 49 | * @return {Sring | Object} completion string, if rawApi == false (default), else return the raw API JSON response 50 | */ 51 | async function promptToCompletion(inConfig) { 52 | // Normalzied string prompt to object 53 | if (typeof inConfig === 'string' || inConfig instanceof String) { 54 | inConfig = { prompt: inConfig }; 55 | } 56 | 57 | // Join it together 58 | let reqJson = Object.assign({}, defaultConfig, inConfig); 59 | 60 | // Extract and remove internal props 61 | let useRawApi = reqJson.rawApi || false; 62 | delete reqJson.rawApi; 63 | 64 | // Normalize "max_tokens" auto 65 | if( reqJson.max_tokens == "auto" ) { 66 | let tokenObj = tokenizer.encode( reqJson.prompt ); 67 | reqJson.max_tokens = 4000 - tokenObj.bpe.length; 68 | if( reqJson.max_tokens < 50 ) { 69 | throw "Prompt is too large for auto mode, total prompt token size is : "+tokenObj.bpe.length; 70 | } 71 | } 72 | 73 | // The return data to use 74 | let respJson = null; 75 | let respErr = null; 76 | 77 | // Retry mechanic, for handling request errors 78 | for(let tries=0; tries < 3; ++tries) { 79 | try { 80 | // Perform the JSON request 81 | const resp = await fetch('https://api.openai.com/v1/completions', { 82 | method: 'post', 83 | body: JSON.stringify(reqJson), 84 | headers: { 85 | 'Content-Type': 'application/json', 86 | "Authorization": `Bearer ${openai_key}` 87 | } 88 | }); 89 | respJson = await resp.json(); 90 | 91 | // Check for response 92 | if( respJson.choices && respJson.choices[0] && respJson.choices[0].text ) { 93 | // Return the JSON as it is 94 | if( useRawApi ) { 95 | return respJson; 96 | } 97 | 98 | // Return the completion in simple mode 99 | return respJson.choices[0].text.trim(); 100 | } 101 | } catch(e) { 102 | respErr = e; 103 | } 104 | } 105 | 106 | // Handle unexpected response 107 | if( respErr ) { 108 | console.warn([ 109 | "## Unable to handle prompt for ...", 110 | JSON.stringify(reqJson), 111 | "## Recieved error ...", 112 | respErr 113 | ].join("\n")); 114 | } else { 115 | console.warn([ 116 | "## Unable to handle prompt for ...", 117 | JSON.stringify(reqJson), 118 | "## Recieved response ...", 119 | JSON.stringify(respJson) 120 | ].join("\n")); 121 | } 122 | throw Error("Missing valid openai response, please check warn logs for more details") 123 | } 124 | 125 | // Export the module 126 | module.exports = promptToCompletion; 127 | 128 | // Special direct execution handling 129 | // https://stackoverflow.com/questions/6398196/detect-if-called-through-require-or-directly-by-command-line 130 | if( require.main === module ) { 131 | (async function() { 132 | 133 | // Argument check 134 | const reqPrompt = process.argv[2]; 135 | if( reqPrompt == null ) { 136 | console.error( "[ERROR] Missing request prompt as first argument" ); 137 | console.error( `[ERROR] Example: node ./promptToCompletion.js "tell me a joke?"` ); 138 | process.exit(1); 139 | } 140 | 141 | // Response 142 | const resp = await promptToCompletion(reqPrompt); 143 | console.log(resp); 144 | })(); 145 | } -------------------------------------------------------------------------------- /src/util/fileExist.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | /** 4 | * Asynchronously checks if a file exists. 5 | * 6 | * Eugene notes: Honestly this should be just a native nodejs command 7 | * 8 | * @param {string} filepath - The path to the file to check. 9 | * @returns {Promise} A promise that resolves to a boolean indicating whether the file exists or not. 10 | */ 11 | async function fileExist(filepath) { 12 | return new Promise((good,bad) => { 13 | try { 14 | fs.stat(filepath, (err, stats) => { 15 | if(err) { 16 | good(false); 17 | } else { 18 | if( stats.isFile() ) { 19 | good(true); 20 | } else { 21 | good(false); 22 | } 23 | } 24 | }); 25 | } catch(e) { 26 | bad(e); 27 | } 28 | }); 29 | } 30 | 31 | module.exports = fileExist; -------------------------------------------------------------------------------- /src/util/getMarkdownObj.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------- 2 | // Dependencies 3 | //-------------------------------------------------- 4 | 5 | const parseMD = require('parse-md').default 6 | const fs = require("fs"); 7 | const path = require('path'); 8 | 9 | //-------------------------------------------------- 10 | // Implementation 11 | //-------------------------------------------------- 12 | 13 | /** 14 | * Get the markdown string, with seperated metadata / frontmatter str 15 | * 16 | * @param {String} filePath for the full markdown file 17 | * 18 | * @return Generated code string, wrapped in a promise 19 | */ 20 | async function getMarkdownObj( filePath ) { 21 | // Read the file, and split out the metadata + content 22 | let fullFileData = await fs.promises.readFile( filePath, "utf8" ); 23 | let { metadata, content } = parseMD(fullFileData); 24 | let frontmatter = ""; 25 | 26 | // Extract frontmatter string 27 | if( fullFileData.startsWith("---") ) { 28 | frontmatter = "---"+fullFileData.split("---")[1]+"---"; 29 | } 30 | 31 | // Return all 3 items 32 | return { 33 | metadata, 34 | frontmatter, 35 | content 36 | } 37 | } 38 | module.exports = getMarkdownObj; 39 | -------------------------------------------------------------------------------- /src/util/indentString.js: -------------------------------------------------------------------------------- 1 | module.exports = function indentString(str, count = 1, indent = ' ', includeEmptyLines = true) { 2 | if (count === 0) { 3 | return str; 4 | } 5 | const regex = includeEmptyLines ? /^/gm : /^(?!\s*$)/gm; 6 | return str.replace(regex, indent.repeat(count)); 7 | } -------------------------------------------------------------------------------- /src/util/scanDirForFiles.js: -------------------------------------------------------------------------------- 1 | // Load the required libraries 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | 5 | /** 6 | * Recursively scan for *.prompt.md files 7 | * And return all the found paths 8 | * 9 | * @param {String} sourcePath to scan from 10 | * @param {Array} fileNameRegexArr to use for matching, use empty array or null to match all filenames 11 | * @param {Array} scanDir to scan/filter against, default null scans everything 12 | * @param {Array} excludeDir to scan/filter against, default null scans everything 13 | * @param {Boolean} relativePath from the sourcePath is return instead 14 | * 15 | * @return {Array} list of various files, in full absolute path form 16 | **/ 17 | async function scanDirForFiles( sourcePath, fileNameRegexArr, scanDir = null, excludeDir = [".git"], relativePath = true ) { 18 | 19 | // resolve sourcpath first 20 | sourcePath = path.resolve(sourcePath); 21 | 22 | // Check that current sourcePath is a directory, if not abort 23 | if( !(await fs.promises.stat(sourcePath)).isDirectory() ) { 24 | return []; 25 | } 26 | 27 | // Prepare the response array 28 | let retArray = []; 29 | 30 | // Scan dir is provided, use that instead 31 | if( scanDir != null ) { 32 | for(let i=0; i= 0 ) { 46 | continue; 47 | } 48 | 49 | // Join as a path 50 | let filePath = path.resolve( sourcePath, filename ); 51 | 52 | // Check if its a folder 53 | const stat = await fs.promises.stat(filePath); 54 | if (stat.isDirectory()) { 55 | // Recurssion 56 | const recursionRes = await scanDirForFiles( filePath, fileNameRegexArr, null, null, false ); 57 | 58 | // Merge it together 59 | retArray.push(recursionRes); 60 | } else { 61 | // Is matching 62 | let matching = false; 63 | if(fileNameRegexArr == null || fileNameRegexArr.length == 0) { 64 | matching = true; 65 | } 66 | // Check if its a matching file 67 | for( const fileRegex of fileNameRegexArr ) { 68 | if( fileRegex.test(filename) ) { 69 | // Add path the ret array 70 | matching = true; 71 | break; 72 | } 73 | } 74 | 75 | // Add if its matching 76 | if(matching) { 77 | retArray.push( filePath ); 78 | } 79 | // Else does nothing, file didn't match 80 | } 81 | } 82 | } 83 | 84 | // Flatten the array 85 | retArray = retArray.flat(); 86 | 87 | // Return the full retArray 88 | // as no path processing is required 89 | if( relativePath == false ) { 90 | return retArray; 91 | } 92 | 93 | // Return the relative path instead 94 | return retArray.map((filePath) => { 95 | return path.relative( sourcePath, filePath ); 96 | }); 97 | } 98 | module.exports = scanDirForFiles; 99 | -------------------------------------------------------------------------------- /src/version.js: -------------------------------------------------------------------------------- 1 | module.exports = "1.0.5-POC" --------------------------------------------------------------------------------